presentation/QuickTaskMenu.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "QuickTaskMenu.h" | ||
| 2 | |||
| 3 | #include "util/MenuText.h" | ||
| 4 | |||
| 5 | #include <QApplication> | ||
| 6 | #include <QColor> | ||
| 7 | #include <QIcon> | ||
| 8 | #include <QMenu> | ||
| 9 | #include <QPainter> | ||
| 10 | #include <QPixmap> | ||
| 11 | #include <QWindow> | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | // A list's colour as a menu icon (F94-35): a filled dot like the sidebar's. | ||
| 15 | ✗ | QIcon colorDot(const QString &color) { | |
| 16 | ✗ | QPixmap pixmap(32, 32); | |
| 17 | ✗ | pixmap.fill(Qt::transparent); | |
| 18 | ✗ | QPainter painter(&pixmap); | |
| 19 | ✗ | painter.setRenderHint(QPainter::Antialiasing); | |
| 20 | ✗ | painter.setPen(Qt::NoPen); | |
| 21 | ✗ | painter.setBrush(QColor(color)); | |
| 22 | ✗ | painter.drawEllipse(QRectF(8, 8, 16, 16)); | |
| 23 | ✗ | return QIcon(pixmap); | |
| 24 | ✗ | } | |
| 25 | ✗ | QIcon iconFor(const QVariantMap &entry) { | |
| 26 | ✗ | const QString color = entry.value("color").toString(); | |
| 27 | ✗ | if (!color.isEmpty() && QColor::isValidColorName(color)) return colorDot(color); | |
| 28 | ✗ | return QIcon::fromTheme(entry.value("icon").toString()); | |
| 29 | ✗ | } | |
| 30 | 1 | QMenu *nativeMenu(const QString &name) { | |
| 31 |
2/5✓ Branch 3 → 4 taken 1 time.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
|
1 | auto *menu = new QMenu; |
| 32 | 1 | menu->setObjectName(name); | |
| 33 | 1 | menu->setAttribute(Qt::WA_DeleteOnClose); | |
| 34 | 1 | return menu; | |
| 35 | } | ||
| 36 | 1 | void popupAt(QMenu *menu, QWindow *parent, qreal x, qreal y) { | |
| 37 | 1 | menu->winId(); | |
| 38 |
1/2✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
|
1 | if (auto *handle = menu->windowHandle()) handle->setTransientParent(parent); |
| 39 |
2/2✓ Branch 7 → 8 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
|
1 | menu->popup(parent->mapToGlobal(QPointF(x, y)).toPoint()); |
| 40 | 1 | } | |
| 41 | } | ||
| 42 | |||
| 43 | 1 | bool QuickTaskMenu::show(const QVariantMap &task, const QVariantList &lists, QObject *window, qreal x, qreal y, const QString §ion) { | |
| 44 | 1 | auto *parent = qobject_cast<QWindow *>(window); | |
| 45 |
4/7✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1 time.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1 time.
|
1 | if (!parent || !qobject_cast<QApplication *>(QCoreApplication::instance())) return false; |
| 46 |
2/2✓ Branch 11 → 12 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
|
1 | auto *menu = nativeMenu("nativeTaskMenu"); |
| 47 |
3/3✓ Branch 15 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 17 → 18 taken 1 time.
|
1 | const bool writable = !task.value("readOnly").toBool(); |
| 48 | 5 | const auto add = [this](QMenu *target, const QString &icon, const QString &label, const QString &action, const QVariantMap &fields = {}) { | |
| 49 | // S106-280: list and account names are shown as written. | ||
| 50 |
3/3✓ Branch 2 → 3 taken 5 times.
✓ Branch 3 → 4 taken 5 times.
✓ Branch 4 → 5 taken 5 times.
|
5 | auto *entry = target->addAction(QIcon::fromTheme(icon), MenuText::plain(label)); |
| 51 |
2/2✓ Branch 7 → 8 taken 5 times.
✓ Branch 8 → 9 taken 5 times.
|
5 | entry->setObjectName("taskMenu-" + action); |
| 52 |
1/3✓ Branch 12 → 13 taken 5 times.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
|
5 | connect(entry, &QAction::triggered, this, [this, action, fields] { emit actionRequested(action, fields); }); |
| 53 | 5 | return entry; | |
| 54 | 1 | }; | |
| 55 |
1/2✗ Branch 22 → 23 not taken.
✓ Branch 22 → 60 taken 1 time.
|
1 | if (section.isEmpty()) { |
| 56 | ✗ | add(menu, writable ? "document-edit" : "document-open", writable ? tr("Edit") : tr("Open"), "edit"); | |
| 57 | ✗ | if (writable) { | |
| 58 | ✗ | const bool completed = task.value("completed").toBool(); | |
| 59 | ✗ | add(menu, completed ? "edit-undo" : "checkmark", completed ? tr("Reopen") : tr("Complete"), "toggle"); | |
| 60 | } | ||
| 61 | } | ||
| 62 |
4/8✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 66 not taken.
✓ Branch 62 → 63 taken 1 time.
✗ Branch 62 → 65 not taken.
✓ Branch 64 → 65 taken 1 time.
✗ Branch 64 → 66 not taken.
✓ Branch 67 → 68 taken 1 time.
✗ Branch 67 → 141 not taken.
|
1 | if (writable && (section.isEmpty() || section == "due")) { |
| 63 | 1 | QMenu *due = menu; | |
| 64 |
1/6✗ Branch 69 → 70 not taken.
✓ Branch 69 → 78 taken 1 time.
✗ Branch 70 → 71 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
|
1 | if (section != "due") { due = menu->addMenu(QIcon::fromTheme("view-calendar-day"), tr("Due")); } |
| 65 |
6/8✓ Branch 80 → 81 taken 1 time.
✓ Branch 81 → 82 taken 1 time.
✓ Branch 82 → 83 taken 1 time.
✓ Branch 83 → 84 taken 1 time.
✓ Branch 89 → 90 taken 1 time.
✓ Branch 89 → 91 taken 1 time.
✗ Branch 472 → 473 not taken.
✗ Branch 472 → 474 not taken.
|
2 | add(due, "go-jump-today", tr("Today"), "due", {{"due", "today"}}); |
| 66 |
6/8✓ Branch 93 → 94 taken 1 time.
✓ Branch 94 → 95 taken 1 time.
✓ Branch 95 → 96 taken 1 time.
✓ Branch 96 → 97 taken 1 time.
✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 104 taken 1 time.
✗ Branch 494 → 495 not taken.
✗ Branch 494 → 496 not taken.
|
2 | add(due, "go-next", tr("Tomorrow"), "due", {{"due", "tomorrow"}}); |
| 67 |
6/8✓ Branch 106 → 107 taken 1 time.
✓ Branch 107 → 108 taken 1 time.
✓ Branch 108 → 109 taken 1 time.
✓ Branch 109 → 110 taken 1 time.
✓ Branch 115 → 116 taken 1 time.
✓ Branch 115 → 117 taken 1 time.
✗ Branch 516 → 517 not taken.
✗ Branch 516 → 518 not taken.
|
2 | add(due, "view-calendar-week", tr("Next week"), "due", {{"due", "nextweek"}}); |
| 68 |
4/4✓ Branch 118 → 119 taken 1 time.
✓ Branch 119 → 120 taken 1 time.
✓ Branch 120 → 121 taken 1 time.
✓ Branch 121 → 122 taken 1 time.
|
1 | add(due, "view-calendar", tr("Pick date…"), "pick"); |
| 69 |
1/1✓ Branch 126 → 127 taken 1 time.
|
1 | due->addSeparator(); |
| 70 |
6/8✓ Branch 129 → 130 taken 1 time.
✓ Branch 130 → 131 taken 1 time.
✓ Branch 131 → 132 taken 1 time.
✓ Branch 132 → 133 taken 1 time.
✓ Branch 138 → 139 taken 1 time.
✓ Branch 138 → 140 taken 1 time.
✗ Branch 550 → 551 not taken.
✗ Branch 550 → 552 not taken.
|
2 | add(due, "edit-clear", tr("No date"), "due", {{"due", ""}}); |
| 71 | } | ||
| 72 |
4/8✓ Branch 141 → 142 taken 1 time.
✗ Branch 141 → 147 not taken.
✓ Branch 143 → 144 taken 1 time.
✗ Branch 143 → 146 not taken.
✗ Branch 145 → 146 not taken.
✓ Branch 145 → 147 taken 1 time.
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 212 taken 1 time.
|
1 | if (writable && (section.isEmpty() || section == "priority")) { |
| 73 | ✗ | QMenu *priority = menu; | |
| 74 | ✗ | if (section != "priority") priority = menu->addMenu(QIcon::fromTheme("flag"), tr("Priority")); | |
| 75 | ✗ | add(priority, "arrow-up", tr("High"), "priority", {{"priority", 1}}); | |
| 76 | ✗ | add(priority, "arrow-right", tr("Normal"), "priority", {{"priority", 5}}); | |
| 77 | ✗ | add(priority, "arrow-down", tr("Low"), "priority", {{"priority", 9}}); | |
| 78 | ✗ | add(priority, "edit-clear", tr("None"), "priority", {{"priority", 0}}); | |
| 79 | } | ||
| 80 |
4/8✓ Branch 212 → 213 taken 1 time.
✗ Branch 212 → 218 not taken.
✓ Branch 214 → 215 taken 1 time.
✗ Branch 214 → 217 not taken.
✗ Branch 216 → 217 not taken.
✓ Branch 216 → 218 taken 1 time.
✗ Branch 219 → 220 not taken.
✓ Branch 219 → 329 taken 1 time.
|
1 | if (writable && (section.isEmpty() || section == "move")) { |
| 81 | ✗ | QMenu *move = menu; | |
| 82 | ✗ | if (section != "move") move = menu->addMenu(QIcon::fromTheme("go-jump"), tr("Move to list")); | |
| 83 | ✗ | for (const auto &value : lists) { | |
| 84 | ✗ | const auto list = value.toMap(); | |
| 85 | ✗ | if (list.value("readOnly").toBool() || !list.value("supportsTasks").toBool()) continue; | |
| 86 | ✗ | auto *entry = add(move, {}, list.value("displayName").toString() + " · " + list.value("accountName").toString(), "move", | |
| 87 | ✗ | {{"accountId", list.value("accountId")}, {"calendarPath", list.value("path")}}); | |
| 88 | ✗ | entry->setIcon(iconFor({{"color", list.value("color")}})); | |
| 89 | ✗ | } | |
| 90 | } | ||
| 91 |
1/2✗ Branch 330 → 331 not taken.
✓ Branch 330 → 402 taken 1 time.
|
1 | if (section.isEmpty()) { |
| 92 | ✗ | menu->addSeparator(); | |
| 93 | ✗ | if (writable) add(menu, "list-add", tr("Add subtask"), "subtask"); | |
| 94 | ✗ | if (!task.value("relatedMessageId").toString().isEmpty()) add(menu, "mail-message", tr("Open linked mail"), "mail"); | |
| 95 | ✗ | add(menu, "appointment-new", tr("Create appointment from task"), "appointment"); | |
| 96 | ✗ | add(menu, "edit-copy", tr("Copy as text"), "copy"); | |
| 97 | ✗ | add(menu, "document-export", tr("Export as .ics"), "export"); | |
| 98 | ✗ | if (writable) { menu->addSeparator(); add(menu, "edit-delete", tr("Delete"), "delete"); } | |
| 99 | } | ||
| 100 |
1/1✓ Branch 402 → 403 taken 1 time.
|
1 | popupAt(menu, parent, x, y); |
| 101 | 1 | return true; | |
| 102 | 4 | } | |
| 103 | |||
| 104 | ✗ | bool QuickTaskMenu::showChoices(const QString &requester, const QVariantList &entries, QObject *window, qreal x, qreal y) { | |
| 105 | ✗ | auto *parent = qobject_cast<QWindow *>(window); | |
| 106 | ✗ | if (!parent || !qobject_cast<QApplication *>(QCoreApplication::instance())) return false; | |
| 107 | ✗ | auto *menu = nativeMenu("nativeTaskChoices-" + requester); | |
| 108 | ✗ | for (const auto &value : entries) { | |
| 109 | ✗ | const auto entry = value.toMap(); | |
| 110 | ✗ | if (entry.value("separator").toBool()) { menu->addSeparator(); continue; } | |
| 111 | ✗ | auto *action = menu->addAction(iconFor(entry), MenuText::plain(entry.value("label").toString())); | |
| 112 | ✗ | const QString id = entry.value("id").toString(); | |
| 113 | ✗ | action->setObjectName("taskChoice-" + id); | |
| 114 | ✗ | action->setCheckable(entry.value("checkable").toBool()); | |
| 115 | ✗ | action->setChecked(entry.value("checked").toBool()); | |
| 116 | // F100-45: single choices without check marks open on their current value. | ||
| 117 | ✗ | if (entry.value("current").toBool()) menu->setActiveAction(action); | |
| 118 | ✗ | connect(action, &QAction::triggered, this, [this, requester, id] { emit choiceTriggered(requester, id); }); | |
| 119 | ✗ | } | |
| 120 | ✗ | popupAt(menu, parent, x, y); | |
| 121 | ✗ | return true; | |
| 122 | } | ||
| 123 |