File indexing completed on 2025-01-19 11:19:01
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