File indexing completed on 2024-02-25 05:42:14

0001 /*
0002     SPDX-FileCopyrightText: 2017-2018 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "activitiesdelegate.h"
0007 
0008 // local
0009 #include "../../generic/persistentmenu.h"
0010 #include "../layoutsmodel.h"
0011 #include "../../generic/generictools.h"
0012 #include "../../../data/layoutdata.h"
0013 
0014 // Qt
0015 #include <QApplication>
0016 #include <QDebug>
0017 #include <QDialogButtonBox>
0018 #include <QMenu>
0019 #include <QModelIndex>
0020 #include <QPainter>
0021 #include <QPushButton>
0022 #include <QString>
0023 #include <QTextDocument>
0024 #include <QWidget>
0025 #include <QWidgetAction>
0026 
0027 // KDE
0028 #include <KLocalizedString>
0029 
0030 #define OKPRESSED "OKPRESSED"
0031 
0032 namespace Latte {
0033 namespace Settings {
0034 namespace Layout {
0035 namespace Delegate {
0036 
0037 Activities::Activities(QObject *parent)
0038     : QStyledItemDelegate(parent)
0039 {
0040 }
0041 
0042 QWidget *Activities::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0043 {
0044     QPushButton *button = new QPushButton(parent);
0045 
0046     PersistentMenu *menu = new PersistentMenu(button);
0047     button->setMenu(menu);
0048     menu->setMinimumWidth(option.rect.width());
0049 
0050     bool isLayoutActive = index.data(Model::Layouts::ISACTIVEROLE).toBool();
0051 
0052     QStringList allActivities = index.data(Model::Layouts::ALLACTIVITIESSORTEDROLE).toStringList();
0053     Latte::Data::ActivitiesTable allActivitiesTable = index.data(Model::Layouts::ALLACTIVITIESDATAROLE).value<Latte::Data::ActivitiesTable>();
0054 
0055     QStringList assignedActivities = index.data(Qt::UserRole).toStringList();
0056 
0057     QList<int> originalChecked;
0058 
0059     QString currentrealactivityid;
0060 
0061     for (int i=0; i<allActivitiesTable.rowCount(); ++i) {
0062         if (allActivitiesTable[i].isCurrent) {
0063             currentrealactivityid = allActivitiesTable[i].id;
0064             break;
0065         }
0066     }
0067 
0068     for (int i=0; i<allActivities.count(); ++i) {
0069         Latte::Data::Activity activitydata = allActivitiesTable[allActivities[i]];
0070 
0071         if (!activitydata.isValid()) {
0072             continue;
0073         }
0074 
0075 
0076         bool inCurrentActivity = (activitydata.id == Data::Layout::CURRENTACTIVITYID && assignedActivities.contains(currentrealactivityid));
0077         bool ischecked = assignedActivities.contains(activitydata.id) || inCurrentActivity;
0078 
0079         if (ischecked) {
0080             originalChecked << i;
0081         }
0082 
0083         QAction *action = new QAction(activitydata.name);
0084         action->setData(activitydata.id);
0085         action->setIcon(QIcon::fromTheme(activitydata.icon));
0086         action->setCheckable(true);
0087         action->setChecked(ischecked);
0088 
0089         if (activitydata.id == Data::Layout::FREEACTIVITIESID
0090                 || activitydata.id == Data::Layout::ALLACTIVITIESID
0091                 || activitydata.id == Data::Layout::CURRENTACTIVITYID) {
0092             if (isLayoutActive) {
0093                 QFont font = action->font();
0094                 font.setBold(true);
0095                 action->setFont(font);
0096             }
0097 
0098             if (ischecked) {
0099                 menu->setMasterIndex(i);
0100             }
0101 
0102             connect(action, &QAction::toggled, this, [this, menu, button, action, i, allActivitiesTable, currentrealactivityid]() {
0103                 if (action->isChecked()) {
0104                     menu->setMasterIndex(i);
0105 
0106                     if (action->data().toString() == Data::Layout::CURRENTACTIVITYID) {
0107                         auto actions = menu->actions();
0108                         for (int i=0; i<actions.count(); ++i) {
0109                             if (actions[i]->data().toString() == currentrealactivityid) {
0110                                 actions[i]->setChecked(true);
0111                             }
0112                         }
0113                     }
0114                 } else {
0115                     if (menu->masterIndex() == i) {
0116                         menu->setMasterIndex(-1);
0117                     }
0118 
0119                     if (action->data().toString() == Data::Layout::CURRENTACTIVITYID) {
0120                         auto actions = menu->actions();
0121                         for (int i=0; i<actions.count(); ++i) {
0122                             if (actions[i]->data().toString() == currentrealactivityid) {
0123                                 actions[i]->setChecked(false);
0124                             }
0125                         }
0126 
0127                         updateCurrentActivityAction(menu);
0128                     }
0129                 }              
0130 
0131                 updateButton(button, allActivitiesTable);
0132             });            
0133         } else {
0134             if (activitydata.isRunning()) {
0135                 QFont font = action->font();
0136                 font.setBold(true);
0137                 action->setFont(font);
0138             }
0139 
0140             connect(action, &QAction::toggled, this, [this, menu, button, action, i, allActivitiesTable]() {
0141                 if (action->isChecked()) {
0142                     menu->setMasterIndex(-1);
0143                 }
0144 
0145                 updateButton(button, allActivitiesTable);
0146             });
0147         }
0148 
0149         menu->addAction(action);
0150 
0151         if (activitydata.id == Data::Layout::CURRENTACTIVITYID) {
0152             //! After CurrentActivity record we can add Separator
0153             menu->addSeparator();
0154         }
0155     }
0156 
0157     connect(menu, &PersistentMenu::masterIndexChanged, this, [this, menu, button, allActivitiesTable]() {
0158         int masterRow = menu->masterIndex();
0159         if (masterRow>=0) {
0160             auto actions = button->menu()->actions();
0161 
0162             for (int i=0; i<actions.count(); ++i) {
0163                 if (i != masterRow && actions[i]->isChecked()) {
0164                     actions[i]->setChecked(false);
0165                 }
0166             }
0167         } else {
0168             foreach (QAction *action, button->menu()->actions()) {
0169                 QString actId = action->data().toString();
0170                 if (actId == Data::Layout::FREEACTIVITIESID || actId == Data::Layout::ALLACTIVITIESID) {
0171                     action->setChecked(false);
0172                 }
0173             }
0174         }
0175 
0176         updateCurrentActivityAction(menu);
0177         updateButton(button, allActivitiesTable);
0178     });
0179 
0180     //! Ok, Apply Buttons behavior
0181     menu->addSeparator();
0182 
0183     QDialogButtonBox *menuDialogButtons = new QDialogButtonBox(menu);
0184     menuDialogButtons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
0185     menuDialogButtons->setContentsMargins(3, 0, 3, 3);
0186 
0187     QWidgetAction* menuDialogButtonsWidgetAction = new QWidgetAction(menu);
0188     menuDialogButtonsWidgetAction->setDefaultWidget(menuDialogButtons);
0189 
0190     menu->addAction(menuDialogButtonsWidgetAction);
0191 
0192     connect(menuDialogButtons->button(QDialogButtonBox::Ok), &QPushButton::clicked,  [this, menu, button]() {
0193         button->setProperty(OKPRESSED, true);
0194         menu->hide();
0195     });
0196 
0197     connect(menuDialogButtons->button(QDialogButtonBox::Cancel), &QPushButton::clicked,  menu, &QMenu::hide);
0198 
0199     connect(menuDialogButtons->button(QDialogButtonBox::Reset), &QPushButton::clicked,  [this, menu, originalChecked]() {
0200         for (int i=0; i<menu->actions().count(); ++i) {
0201             if (!originalChecked.contains(i)) {
0202                 menu->actions().at(i)->setChecked(false);
0203             } else {
0204                 menu->actions().at(i)->setChecked(true);
0205             }
0206         }
0207     });
0208 
0209     connect(menu, &QMenu::aboutToHide, button, &QWidget::clearFocus);
0210 
0211     return button;
0212 }
0213 
0214 void Activities::updateCurrentActivityAction(QMenu *menu) const
0215 {
0216     if (!menu) {
0217         return;
0218     }
0219 
0220     auto actions = menu->actions();
0221     for (int i=0; i<actions.count(); ++i) {
0222         if (actions[i]->data().toString() == Data::Layout::CURRENTACTIVITYID) {
0223             if (actions[i]->isChecked()) {
0224                 QFont font = actions[i]->font();
0225                 font.setBold(true);
0226                 actions[i]->setFont(font);
0227             } else {
0228                 QFont font = actions[i]->font();
0229                 font.setBold(false);
0230                 actions[i]->setFont(font);
0231             }
0232         }
0233     }
0234 
0235 }
0236 
0237 void Activities::setEditorData(QWidget *editor, const QModelIndex &index) const
0238 {
0239     Latte::Data::ActivitiesTable allActivitiesTable = index.data(Model::Layouts::ALLACTIVITIESDATAROLE).value<Latte::Data::ActivitiesTable>();
0240 
0241     updateButton(editor, allActivitiesTable);
0242 }
0243 
0244 void Activities::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0245 {
0246     QPushButton *button = static_cast<QPushButton *>(editor);
0247 
0248     if (button->property(OKPRESSED).isNull() || !button->property(OKPRESSED).toBool()) {
0249         return;
0250     }
0251 
0252     //! keep activities that are present in other computers
0253     QStringList assignedActivities = index.data(Qt::UserRole).toStringList();
0254 
0255     foreach (QAction *action, button->menu()->actions()) {
0256         QString activityid = action->data().toString();
0257 
0258         if (activityid == Data::Layout::CURRENTACTIVITYID) {
0259             continue;
0260         }
0261 
0262         if (activityid == Data::Layout::ALLACTIVITIESID && action->isChecked()) {
0263             assignedActivities = QStringList(Data::Layout::ALLACTIVITIESID);
0264             break;
0265         } else if (activityid == Data::Layout::FREEACTIVITIESID && action->isChecked()) {
0266             assignedActivities = QStringList(Data::Layout::FREEACTIVITIESID);
0267             break;
0268         }
0269 
0270         //! try to not remove activityids that belong to different machines that are not
0271         //! currently present
0272         if (!action->isChecked()) {
0273             assignedActivities.removeAll(activityid);
0274         } else if (action->isChecked() && !assignedActivities.contains(activityid)) {
0275             assignedActivities << activityid;
0276         }
0277     }
0278 
0279     model->setData(index, assignedActivities, Qt::UserRole);
0280 }
0281 
0282 void Activities::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0283 {
0284     editor->setGeometry(option.rect);
0285 }
0286 
0287 bool Activities::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
0288                              const QModelIndex &index)
0289 {
0290     Q_ASSERT(event);
0291     Q_ASSERT(model);
0292 
0293     return QStyledItemDelegate::editorEvent(event, model, option, index);
0294 }
0295 
0296 void Activities::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0297 {
0298     QStyleOptionViewItem myOptions = option;
0299     //! Remove the focus dotted lines
0300     myOptions.state = (myOptions.state & ~QStyle::State_HasFocus);
0301 
0302     bool isLayoutActive = index.data(Model::Layouts::ISACTIVEROLE).toBool();
0303 
0304     QList<Latte::Data::Activity> assignedActivities;
0305     QStringList assignedIds = index.model()->data(index, Qt::UserRole).toStringList();
0306     QStringList assignedOriginalIds = index.model()->data(index, Model::Layouts::ORIGINALASSIGNEDACTIVITIESROLE).toStringList();
0307 
0308     Latte::Data::ActivitiesTable allActivitiesTable = index.data(Model::Layouts::ALLACTIVITIESDATAROLE).value<Latte::Data::ActivitiesTable>();
0309 
0310     for (int i=0; i<assignedIds.count(); ++i) {
0311         assignedActivities << allActivitiesTable[assignedIds[i]];
0312     }
0313 
0314     if (assignedActivities.count() > 0) {
0315         myOptions.text = joinedActivities(assignedActivities, assignedOriginalIds, isLayoutActive);
0316     } else {
0317         myOptions.text = "";
0318     }
0319 
0320     Latte::drawBackground(painter, option);
0321     Latte::drawFormattedText(painter, myOptions);
0322 }
0323 
0324 QString Activities::joinedActivities(const QList<Latte::Data::Activity> &activities, const QStringList &originalIds, bool isActive, bool formatText) const
0325 {
0326     QString finalText;
0327 
0328     int i = 0;
0329 
0330     for (int i=0; i<activities.count(); ++i) {
0331         bool bold{false};
0332         bool italic = (!originalIds.contains(activities[i].id));
0333 
0334         if (activities[i].id == Data::Layout::FREEACTIVITIESID || activities[i].id == Data::Layout::ALLACTIVITIESID) {
0335             bold = isActive;
0336         } else {
0337             bold = activities[i].isRunning();
0338         }
0339 
0340         if (i > 0) {
0341             finalText += ", ";
0342         }
0343 
0344         QString styledText = activities[i].name;
0345 
0346         if (bold && formatText) {
0347             styledText = "<b>" + styledText + "</b>";
0348         }
0349 
0350         if (italic && formatText) {
0351             styledText = "<i>" + styledText + "</i>";
0352         }
0353 
0354         finalText += styledText;
0355     }
0356 
0357     return finalText;
0358 }
0359 
0360 void Activities::updateButton(QWidget *editor, const Latte::Data::ActivitiesTable &allActivitiesTable) const
0361 {
0362     if (!editor) {
0363         return;
0364     }
0365 
0366     QPushButton *button = static_cast<QPushButton *>(editor);
0367     QList<Latte::Data::Activity> assignedActivities;
0368 
0369     foreach (QAction *action, button->menu()->actions()) {
0370         if (action->isChecked() && action->data().toString() != Data::Layout::CURRENTACTIVITYID) {
0371             assignedActivities << allActivitiesTable[action->data().toString()];
0372         }
0373     }
0374 
0375     button->setText(joinedActivities(assignedActivities, QStringList(), false, false));
0376 }
0377 
0378 }
0379 }
0380 }
0381 }
0382