From a36fff5617f16ac1d17c0c52f2112a20c4fa9336 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 19 Aug 2026 10:46:45 +0200 Subject: feat(pane): offer Select all, and report what a copy copied Items 115 and 117, both from the user's notes. Select all was never in Chromium's menu for this pane, measured by hand with a selection active and against a build with removeBrowserActions() reverted, so the filter is not what removed it. MessageView::addPaneActions() supplies it, static and taking the menu, mirroring removeBrowserActions() beside it. Two comments claiming the standard menu already offered it are corrected; either would have sent the next reader down the same three wrong theories the item records. The copy entries all worked and none of them said so. Four now report through the pane's existing statusMessage, each naming what it copied rather than saying "Copied", which is the item's own constraint when three of them sit together in one menu. Connected to the page's own QActions, so the report follows the entry wherever it is triggered from. The two differ in what can be tested, and the tests say so rather than papering over it. The copy path is fully covered: triggering the action runs the production path, and mutations for a duplicated message and an unwired entry both fail. addPaneActions() is covered, but showBodyContextMenu() CALLING it is not and cannot be, since createStandardContextMenu() returns nothing outside a real context-menu event; a mutation deleting that call leaves the suite green, measured. The call site is a hand test and the test file records that so nobody adds an assertion that appears to cover it. The copy strings are QT_TR_NOOP inside an array, which CLAUDE.md warns extracts nothing at file scope. Verified rather than assumed: lupdate found all four under the MessageView context, because the array sits inside a member function. 387 finished, 0 unfinished. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 8 ++ .../2026-08-03-post-0.1.0-usability-closed.md | 124 +++++++++++++++++++++ .../plans/2026-08-03-post-0.1.0-usability.md | 92 +-------------- src/messageview.cpp | 62 ++++++++++- src/messageview.h | 23 +++- tests/test_messageview.cpp | 93 ++++++++++++++++ translations/qtmaildir_it_IT.ts | 16 +++ 7 files changed, 324 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0b90c8..d17b46c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ point at which they are stable. ## [Unreleased] +### Added + +- The message pane's right-click menu offers **Select all**. Chromium's own menu + for this pane has never carried it. +- Copying from the message pane now says what was copied. Copy, Copy link + address, Copy image and Copy image address each report in the status bar, + where they expire like every other transient message. + ## [0.26.0] - 2026-08-19 Delete now moves mail into the account's trash folder instead of only tagging diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md index ef51b5c..783e4ed 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md @@ -6699,3 +6699,127 @@ database directly. **Verified:** clean build with no warnings from any changed file, 24 of 24 suites over three consecutive runs, 211 tests in `test_mainwindow`. Every one of the spec's six testing bullets has a test, each mutation-checked. + +## 115. A copy from the message pane gives no confirmation + +**Observed (user, 2026-08-17):** Copy link address, Copy image address and Copy +image all work, and none of them says so. The user asked for "a small +'link copied' transient that appears and disappears after a few seconds". + +**Cause:** not a defect, unbuilt. These are Chromium's own menu entries and it +does not report success; nothing in this application is listening to them. + +**Copy image was thought to be broken and is not**; see item 116 in the +closed-items file, which is worth reading for how a stale clipboard reading +produced a confident wrong cause. + +All three work. Copy image was briefly filed as broken (item 116) and is not: +the pixels reach the clipboard correctly, and the reading that said otherwise +was taken off a stale clipboard. So this item covers the confirmation for Copy +link address, Copy image address and Copy image alike. + +**Approach.** `MessageView::statusMessage` already exists and the status bar +already expires its messages (item 33), so this is one connection per action and +no new widget, no new timer. `QWebEnginePage::action()` gives each entry's +QAction; connect `triggered` and emit the appropriate string. + +**Constraints.** + +- The message must name what was copied. "Copied" alone is worse than nothing + when three entries sit next to each other in the same menu. +- Do not build a floating overlay for this. The status bar is where this + application reports transient results, and a second mechanism for the same + job is the kind of thing item 45 recorded when two Sync buttons disagreed. +- Depends on nothing; can be built alongside 114 since both touch the same menu. + +**Size: XS.** + +**Built 2026-08-19.** Four entries report through the pane's existing +`statusMessage`: Copy, Copy link address, Copy image and Copy image address, +each naming what it copied rather than saying "Copied". Connected to the PAGE's +own QActions in the constructor, which are the same instances the standard +context menu holds, so the report follows the entry wherever it is triggered +from and no menu of ours is involved. + +That is also what makes this fully testable, unlike item 117 beside it: +triggering the action runs the production path with no context-menu event +needed. The test enables each action first, since Chromium disables a copy entry +when there is nothing of that kind under the cursor and `trigger()` on a +disabled QAction emits nothing at all, which would have left the loop asserting +nothing while looking thorough. Two mutations fail it: a duplicated message, and +one entry left unwired. + +The strings are `QT_TR_NOOP` inside an array, which CLAUDE.md warns extracts +NOTHING at file scope. Verified rather than assumed: `lupdate` found all four +under the `MessageView` context with no warning, because the array sits inside +a member function where the class context exists. + +## 117. The message pane offers no Select all + +**Observed (user, 2026-08-17):** right-clicking a body selection offers Copy and +the search entries, and no Select all. Noticed while hand-testing item 100. + +**NOT caused by item 100, and this was verified rather than argued.** The user +ran a build with `src/messageview.cpp` and `src/messageview.h` reverted to HEAD, +so `removeBrowserActions()` did not exist, and reported the same menu: Copy and +the search entries only. Chromium's standard menu for this pane has never +carried Select all. + +**Three wrong theories preceded that measurement**, which is the part worth +recording, because each was plausible and each cost a round trip: + +1. *The separator sweep removed it.* Disproved with a standalone program + reproducing the sweep against a realistic menu: it drops the leading + separator and keeps every action. +2. *Chromium omits it when there is no selection.* Killed by the user, who had a + selection at the time. +3. *A probe will show what the real menu holds.* `createStandardContextMenu()` + returns NULL outside an actual context-menu event, on the offscreen platform + and on a real display alike, so two probe attempts measured nothing. + +The lesson is the one item 100 had already written down and the agent did not +follow: **a menu built by hand proves nothing about the menu Chromium builds.** +`theBodyMenuDropsTheBrowsersOwnActions` constructs its own QMenu, which is right +for testing the filter and useless for testing what is offered. That test now +says so, and deliberately does NOT assert on SelectAll, since a passing +assertion there would read as a guarantee the code does not make. + +**Approach.** Add it explicitly rather than hoping Chromium supplies it. The +action already exists as `QWebEnginePage::SelectAll` and works; only the menu +entry is missing. + +- `menu->addAction(page->action(QWebEnginePage::SelectAll))` in + `showBodyContextMenu`, placed beside Copy rather than appended after the + search entries. +- Worth considering a `Ctrl+A` binding for the pane at the same time, though + note the pane is not the only focusable widget and the query bar has its own + claim on that key. Check `KeyMap::defaultBindings()` before adding one. + +**Constraints.** + +- **A test for this cannot use a hand-built menu.** That is the trap above. The + honest options are asserting the action is in the menu the production code + returns, which needs a real context-menu event, or leaving it to a hand test + and saying so. Do not write a test that constructs a QMenu and calls it + covered. +- Select all selects the rendered body, not the header label, which is a + separate widget with its own selection. That is probably the desired + behaviour but should be looked at rather than assumed. + +**Size: XS.** + +**Built 2026-08-19** as `MessageView::addPaneActions()`, static and taking the +menu, mirroring `removeBrowserActions()` beside it. Two stale comments went with +it: both `showBodyContextMenu()` and the header of `removeBrowserActions()` +claimed Select all was among the entries the standard menu supplied and that the +filter preserved. Neither was true, and either would have sent the next reader +back down the same three wrong theories. + +**The call site is not covered, and the test says so rather than pretending.** +`addPaneActions()` has a test and a mutation dropping the entry fails it. A +mutation deleting the CALL from `showBodyContextMenu()` leaves the whole suite +green, measured, because `createStandardContextMenu()` returns nothing outside a +real context-menu event and the offscreen platform cannot deliver one. The +honest options the entry named were an assertion on the production menu or a +hand test; the first is impossible here, so it is the second, recorded in the +test so nobody adds an assertion that appears to cover it. diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index 335b3d3..be52da9 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -179,9 +179,9 @@ taking that too literally. | 112 | Toggle unread on a whole thread cannot reach "all unread" on a partly-read thread | defect | S | open, found 2026-08-17. A toggle over a UNION has no direction on a mixed thread | | 113 | No way to see a message's HTML source | information | S | open, 2026-08-17. Chromium's own View source cannot work here; needs our own plain-text dialog. Item 100 removed the dead entry, which was an overreach: the user had not asked for it | | 114 | Save image is offered on every image and does nothing | defect | S | open, found 2026-08-17 by right-clicking a real image. No `downloadRequested` handler exists anywhere, so the request is emitted and never answered | -| 115 | A copy from the message pane gives no confirmation | presentation | XS | open, 2026-08-17. Copy link address, Copy image address and Copy image all work, silently. `statusMessage` already exists and already expires | +| 115 | A copy from the message pane gives no confirmation | presentation | XS | **done** 2026-08-19, unreleased. Four entries report, each naming what it copied; connected to the page's own QActions, so the entry is covered wherever it is triggered from | | 116 | Copy image copies markup instead of the image | defect | XS | **dropped** 2026-08-17, same day. NOT A DEFECT: `wl-paste --list-types` run immediately after a copy reports `image/png`, `application/x-qt-image` and 30 more image flavours. The clipboard is correct and Chromium is behaving. The earlier "text only" reading was taken minutes late off a clipboard that had been overwritten, and a whole cause was theorised on it | -| 117 | The message pane offers no Select all | workflow | XS | open, found 2026-08-17. NOT caused by item 100: verified by hand against a build with that filter reverted, and the menu holds Copy and the search entries either way | +| 117 | The message pane offers no Select all | workflow | XS | **done** 2026-08-19, unreleased. `addPaneActions()` supplies it. The call site is NOT covered by a test and cannot be: the production menu needs a real context-menu event. Stated in the test rather than faked | | 118 | No way to empty the trash from inside the app | workflow | S | open, 2026-08-17. **Blocked on 103**, which creates the trash in the first place. Deliberately left out of 103's spec at the user's request rather than squeezed in | | 119 | The unsynced-changes count cannot be opened to see what it counts | information | S | open, 2026-08-19, from the notes. One of the four things it sums carries no message ids at all, so a list cannot be complete without a change to how the count is kept | @@ -717,94 +717,6 @@ for the network to satisfy it. **Size: S.** -## 115. A copy from the message pane gives no confirmation - -**Observed (user, 2026-08-17):** Copy link address, Copy image address and Copy -image all work, and none of them says so. The user asked for "a small -'link copied' transient that appears and disappears after a few seconds". - -**Cause:** not a defect, unbuilt. These are Chromium's own menu entries and it -does not report success; nothing in this application is listening to them. - -**Copy image was thought to be broken and is not**; see item 116 in the -closed-items file, which is worth reading for how a stale clipboard reading -produced a confident wrong cause. - -All three work. Copy image was briefly filed as broken (item 116) and is not: -the pixels reach the clipboard correctly, and the reading that said otherwise -was taken off a stale clipboard. So this item covers the confirmation for Copy -link address, Copy image address and Copy image alike. - -**Approach.** `MessageView::statusMessage` already exists and the status bar -already expires its messages (item 33), so this is one connection per action and -no new widget, no new timer. `QWebEnginePage::action()` gives each entry's -QAction; connect `triggered` and emit the appropriate string. - -**Constraints.** - -- The message must name what was copied. "Copied" alone is worse than nothing - when three entries sit next to each other in the same menu. -- Do not build a floating overlay for this. The status bar is where this - application reports transient results, and a second mechanism for the same - job is the kind of thing item 45 recorded when two Sync buttons disagreed. -- Depends on nothing; can be built alongside 114 since both touch the same menu. - -**Size: XS.** - -## 117. The message pane offers no Select all - -**Observed (user, 2026-08-17):** right-clicking a body selection offers Copy and -the search entries, and no Select all. Noticed while hand-testing item 100. - -**NOT caused by item 100, and this was verified rather than argued.** The user -ran a build with `src/messageview.cpp` and `src/messageview.h` reverted to HEAD, -so `removeBrowserActions()` did not exist, and reported the same menu: Copy and -the search entries only. Chromium's standard menu for this pane has never -carried Select all. - -**Three wrong theories preceded that measurement**, which is the part worth -recording, because each was plausible and each cost a round trip: - -1. *The separator sweep removed it.* Disproved with a standalone program - reproducing the sweep against a realistic menu: it drops the leading - separator and keeps every action. -2. *Chromium omits it when there is no selection.* Killed by the user, who had a - selection at the time. -3. *A probe will show what the real menu holds.* `createStandardContextMenu()` - returns NULL outside an actual context-menu event, on the offscreen platform - and on a real display alike, so two probe attempts measured nothing. - -The lesson is the one item 100 had already written down and the agent did not -follow: **a menu built by hand proves nothing about the menu Chromium builds.** -`theBodyMenuDropsTheBrowsersOwnActions` constructs its own QMenu, which is right -for testing the filter and useless for testing what is offered. That test now -says so, and deliberately does NOT assert on SelectAll, since a passing -assertion there would read as a guarantee the code does not make. - -**Approach.** Add it explicitly rather than hoping Chromium supplies it. The -action already exists as `QWebEnginePage::SelectAll` and works; only the menu -entry is missing. - -- `menu->addAction(page->action(QWebEnginePage::SelectAll))` in - `showBodyContextMenu`, placed beside Copy rather than appended after the - search entries. -- Worth considering a `Ctrl+A` binding for the pane at the same time, though - note the pane is not the only focusable widget and the query bar has its own - claim on that key. Check `KeyMap::defaultBindings()` before adding one. - -**Constraints.** - -- **A test for this cannot use a hand-built menu.** That is the trap above. The - honest options are asserting the action is in the menu the production code - returns, which needs a real context-menu event, or leaving it to a hand test - and saying so. Do not write a test that constructs a QMenu and calls it - covered. -- Select all selects the rendered body, not the header label, which is a - separate widget with its own selection. That is probably the desired - behaviour but should be looked at rather than assumed. - -**Size: XS.** - ## 118. No way to empty the trash from inside the app **Observed (user, 2026-08-17):** raised while reviewing item 103's spec, as diff --git a/src/messageview.cpp b/src/messageview.cpp index 68821d8..2dba6d1 100644 --- a/src/messageview.cpp +++ b/src/messageview.cpp @@ -158,6 +158,38 @@ MessageView::MessageView(QWidget *parent) connect(m_view, &QWidget::customContextMenuRequested, this, &MessageView::showBodyContextMenu); + // Item 115. Chromium's copy entries all work and none of them says so, so + // the pane reports for them. Connected to the page's own QActions, which + // are the same instances the standard context menu holds, so this covers + // the entry wherever it is triggered from and needs no menu of our own. + // + // Each message names WHAT was copied. "Copied" alone is worse than nothing + // when three of these sit together in one menu. + // + // The status bar rather than a floating overlay, at the item's insistence: + // this is where the application already reports transient results and + // where they already expire (item 33). A second mechanism for one job is + // what item 45 recorded when two Sync buttons disagreed. + static const struct { + QWebEnginePage::WebAction action; + const char *message; + } kCopyReports[] = { + { QWebEnginePage::Copy, QT_TR_NOOP("Copied the selected text") }, + { QWebEnginePage::CopyLinkToClipboard, QT_TR_NOOP("Copied the link address") }, + { QWebEnginePage::CopyImageToClipboard, QT_TR_NOOP("Copied the image") }, + { QWebEnginePage::CopyImageUrlToClipboard, QT_TR_NOOP("Copied the image address") }, + }; + + for (const auto &report : kCopyReports) { + QAction *action = m_view->page()->action(report.action); + if (!action) + continue; + const QString message = tr(report.message); + connect(action, &QAction::triggered, this, [this, message]() { + emit statusMessage(message); + }); + } + // Ctrl+wheel zoom. The filter goes on the application rather than on // m_view: the wheel event is delivered to an internal QQuickWidget the // view creates lazily, so there is no child to filter at this point and a @@ -660,10 +692,31 @@ void MessageView::removeBrowserActions(QMenu *menu, QWebEnginePage *page) menu->removeAction(menu->actions().constLast()); } +void MessageView::addPaneActions(QMenu *menu, QWebEnginePage *page) +{ + if (!menu || !page) + return; + + // Item 117. Added explicitly rather than relied upon: Chromium's standard + // menu for this pane does not offer Select all and never did, measured by + // hand with a selection active and against a build with + // removeBrowserActions() reverted. The filter is not what removed it, so + // relaxing the filter would not bring it back. + // + // The action itself already exists and already works; only the entry was + // missing. + if (QAction *selectAll = page->action(QWebEnginePage::SelectAll)) + menu->addAction(selectAll); +} + void MessageView::showBodyContextMenu(const QPoint &pos) { - // The page's own menu first: copy, select all and the rest stay exactly as - // they were. This adds to that menu rather than replacing it. + // The page's own menu first: Copy and the rest stay exactly as they were. + // This adds to that menu rather than replacing it. + // + // "and select all" used to be in that sentence and was wrong: Chromium's + // menu here has never offered it. Item 117 measured that and addPaneActions() + // supplies it below. QMenu *menu = m_view->createStandardContextMenu(); if (!menu) menu = new QMenu(this); @@ -673,6 +726,11 @@ void MessageView::showBodyContextMenu(const QPoint &pos) // apply here. Item 100. removeBrowserActions(menu, m_view->page()); + // ...plus the ones it needs and Chromium does not supply. Item 117. + // Before the search entries, so it sits with Copy rather than after a + // separator at the bottom. + addPaneActions(menu, m_view->page()); + // selectedText() reads the selection out of the render process with no // script injection. JavaScript is disabled in this profile and stays so. const SearchOffer offer = selectionSearchOffer(m_view->page()->selectedText()); diff --git a/src/messageview.h b/src/messageview.h index df20e40..0b18769 100644 --- a/src/messageview.h +++ b/src/messageview.h @@ -155,8 +155,10 @@ public: /// Item 100. The pane is not a browser: every document arrives through /// setHtml() with a fixed base URL, so Back, Forward, Reload and Save page /// have nothing to act on and the interceptor blocks everything by default - /// anyway. Copy and Select all are the reason the standard menu is used at - /// all, so the menu is filtered, not rebuilt. + /// anyway. Copy and View source are the reason the standard menu is used at + /// all, so the menu is filtered, not rebuilt. Select all is NOT among them: + /// Chromium's menu here has never offered it, which item 117 measured and + /// addPaneActions() supplies. /// /// View source is NOT filtered, though it was at first. It has a real /// document and a real use; item 113 implements it as our own dialog, @@ -170,6 +172,23 @@ public: /// without a rendered document or a shown popup. static void removeBrowserActions(QMenu *menu, QWebEnginePage *page); + /// Adds the entries this pane needs and Chromium's standard menu does not + /// supply: Select all, for now. + /// + /// Item 117. Chromium's menu for this pane has NEVER carried Select all, + /// measured by hand with a selection active and against a build with + /// removeBrowserActions() reverted. Do not assume the standard menu + /// provides it and do not "restore" it by relaxing the filter above, which + /// never removed it. + /// + /// Static and taking the menu for the same reason as removeBrowserActions(): + /// createStandardContextMenu() returns nothing outside a real context-menu + /// event, so the production menu cannot be built in a test at all. A test + /// that hand-builds a QMenu proves what THIS function does and nothing + /// about what Chromium offers, which is the distinction item 117 records + /// after three wrong theories. Keep the two questions separate. + static void addPaneActions(QMenu *menu, QWebEnginePage *page); + /// Tells the pane whether the query bar currently holds anything. /// /// The menus need it to grey out "Exclude from search": excluding from an diff --git a/tests/test_messageview.cpp b/tests/test_messageview.cpp index 87234aa..df41dd1 100644 --- a/tests/test_messageview.cpp +++ b/tests/test_messageview.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ private slots: void headerOffersNothingForAnAbsentField(); void bodySelectionBecomesAQuotedSearch(); void theBodyMenuDropsTheBrowsersOwnActions(); + void theBodyMenuOffersSelectAll(); + void aCopyFromThePaneReportsWhatWasCopied(); void aSearchFromTheDetailsDialogClosesIt(); private: @@ -779,6 +782,96 @@ void TestMessageView::theBodyMenuDropsTheBrowsersOwnActions() QVERIFY(!left.constLast()->isSeparator()); } +void TestMessageView::theBodyMenuOffersSelectAll() +{ + // Item 117. Chromium's standard menu for this pane has NEVER carried + // Select all: measured by hand with a selection active, and against a + // build with removeBrowserActions() reverted, so the filter is not what + // removed it. The pane adds it. + // + // What this test can and cannot prove is the whole point of the item, and + // three wrong theories were bought before it was measured. The production + // menu comes from createStandardContextMenu(), which returns nothing + // outside a real context-menu event, so no test can build it. This + // therefore asserts what addPaneActions() does to a menu handed to it, and + // says NOTHING about what Chromium offers. Those are separate questions; + // conflating them is what item 117 records. + // + // The limit is worth stating precisely, because it is the second half of + // the same trap: this test does NOT cover showBodyContextMenu() CALLING + // addPaneActions(). Measured, a mutation deleting that call leaves the + // whole suite green. Covering it needs a real context-menu event, which the + // offscreen platform cannot deliver, so the call site is a hand test. Do + // not add an assertion here that appears to cover it. + MessageView view; + auto *page = view.findChild(); + QVERIFY2(page, "no page, so this test would assert nothing"); + + QMenu menu; + auto *selectAll = page->action(QWebEnginePage::SelectAll); + QVERIFY2(selectAll, "the page offers no SelectAll action at all"); + + // The guard: absent before, so a pass cannot come from the menu already + // holding it or from the action being added twice by something else. + QVERIFY(!menu.actions().contains(selectAll)); + + MessageView::addPaneActions(&menu, page); + + QVERIFY2(menu.actions().contains(selectAll), + "the pane's menu does not offer Select all"); +} + +void TestMessageView::aCopyFromThePaneReportsWhatWasCopied() +{ + // Item 115. Copy link address, Copy image address and Copy image all work + // and none of them said so. Chromium does not report success, so the pane + // listens to its actions and emits the pane's own status message. + // + // Unlike item 117's entry, this IS fully testable: the connections are made + // to the page's own QActions in the constructor, so triggering one runs the + // production path. No context-menu event is involved. + MessageView view; + auto *page = view.findChild(); + QVERIFY2(page, "no page, so this test would assert nothing"); + + QSignalSpy spy(&view, &MessageView::statusMessage); + QVERIFY(spy.isValid()); + + // Each entry names WHAT was copied. "Copied" alone is worse than nothing + // when three entries sit together in one menu, which the item states as a + // constraint, so the messages are asserted to differ from each other. + const QList copies = { + QWebEnginePage::Copy, + QWebEnginePage::CopyLinkToClipboard, + QWebEnginePage::CopyImageToClipboard, + QWebEnginePage::CopyImageUrlToClipboard, + }; + + QStringList seen; + for (const QWebEnginePage::WebAction which : copies) { + QAction *action = page->action(which); + QVERIFY2(action, "the page offers no action for a copy entry"); + + // Enabled explicitly. Chromium disables a copy action when there is + // nothing of that kind under the cursor, and trigger() on a disabled + // QAction emits nothing at all, so without this the loop would assert + // nothing while looking thorough. + action->setEnabled(true); + + spy.clear(); + action->trigger(); + + QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 1, 5000); + const QString message = spy.takeFirst().at(0).toString(); + QVERIFY2(!message.isEmpty(), "a copy reported an empty status message"); + seen.append(message); + } + + // Four distinct messages, so no two entries report the same thing. + QCOMPARE(seen.size(), copies.size()); + QCOMPARE(QSet(seen.cbegin(), seen.cend()).size(), copies.size()); +} + void TestMessageView::aSearchFromTheDetailsDialogClosesIt() { // The dialog is modal. Without closing it, the query runs and the thread diff --git a/translations/qtmaildir_it_IT.ts b/translations/qtmaildir_it_IT.ts index b9515b4..e7a4b49 100644 --- a/translations/qtmaildir_it_IT.ts +++ b/translations/qtmaildir_it_IT.ts @@ -968,6 +968,22 @@ MessageView + + Copied the selected text + Testo selezionato copiato + + + Copied the link address + Indirizzo del collegamento copiato + + + Copied the image + Immagine copiata + + + Copied the image address + Indirizzo dell'immagine copiato + Details... Dettagli... -- cgit v1.2.3