diff --git a/ftl/core/actions.ftl b/ftl/core/actions.ftl
index bae306621..f2f3bc0b4 100644
--- a/ftl/core/actions.ftl
+++ b/ftl/core/actions.ftl
@@ -5,6 +5,7 @@ actions-cancel = Cancel
actions-choose = Choose
actions-close = Close
actions-copy = Copy
+actions-create-copy = Create Copy
actions-custom-study = Custom Study
actions-decks = Decks
actions-delete = Delete
@@ -49,6 +50,9 @@ actions-update-notetype = Update Notetype
actions-update-config = Update Config
actions-card-info = Card Info
actions-previous-card-info = Previous Card Info
+# By convention, the name of a menu action is suffixed with "..." if additional
+# input is required before it can be performed. E.g. "Export..." vs. "Delete".
+actions-with-ellipsis = { $action }...
## Flags
diff --git a/ftl/core/browsing.ftl b/ftl/core/browsing.ftl
index 5ffc76973..5ac5597ec 100644
--- a/ftl/core/browsing.ftl
+++ b/ftl/core/browsing.ftl
@@ -51,7 +51,7 @@ browsing-interval = Interval
browsing-last-card = Last Card
browsing-learning = (learning)
browsing-line-size = Line Size:
-browsing-manage-note-types = Manage Note Types...
+browsing-manage-note-types = Manage Note Types
browsing-move-cards = Move Cards
browsing-move-cards-to-deck = Move cards to deck:
browsing-new = (new)
diff --git a/ftl/core/notetypes.ftl b/ftl/core/notetypes.ftl
index 5e993c0c2..6bcea398b 100644
--- a/ftl/core/notetypes.ftl
+++ b/ftl/core/notetypes.ftl
@@ -20,16 +20,16 @@ notetypes-card-1-name = Card 1
notetypes-card-2-name = Card 2
notetypes-add = Add: { $val }
notetypes-add-note-type = Add Note Type
-notetypes-cards = Cards...
+notetypes-cards = Cards
notetypes-clone = Clone: { $val }
notetypes-copy = { $val } copy
notetypes-create-scalable-images-with-dvisvgm = Create scalable images with dvisvgm
notetypes-delete-this-note-type-and-all = Delete this note type and all its cards?
notetypes-delete-this-unused-note-type = Delete this unused note type?
-notetypes-fields = Fields...
+notetypes-fields = Fields
notetypes-footer = Footer
notetypes-header = Header
notetypes-note-types = Note Types
-notetypes-options = Options...
+notetypes-options = Options
notetypes-please-add-another-note-type-first = Please add another note type first.
notetypes-type = Type
diff --git a/ftl/qt/qt-accel.ftl b/ftl/qt/qt-accel.ftl
index e2ac4ae80..b34371101 100644
--- a/ftl/qt/qt-accel.ftl
+++ b/ftl/qt/qt-accel.ftl
@@ -1,8 +1,8 @@
-qt-accel-about = &About...
+qt-accel-about = &About
qt-accel-browse-and-install = &Browse and Install...
qt-accel-cards = &Cards
qt-accel-check-database = &Check Database
-qt-accel-check-media = Check &Media...
+qt-accel-check-media = Check &Media
qt-accel-edit = &Edit
qt-accel-exit = E&xit
qt-accel-export = &Export...
@@ -13,7 +13,7 @@ qt-accel-find = &Find
qt-accel-find-and-replace = Find and Re&place...
qt-accel-find-duplicates = Find &Duplicates...
qt-accel-go = &Go
-qt-accel-guide = &Guide...
+qt-accel-guide = &Guide
qt-accel-help = &Help
qt-accel-import = &Import...
qt-accel-info = &Info...
@@ -21,12 +21,12 @@ qt-accel-invert-selection = &Invert Selection
qt-accel-next-card = &Next Card
qt-accel-note = N&ote
qt-accel-notes = &Notes
-qt-accel-open-addons-folder = &Open Add-ons Folder...
-qt-accel-preferences = &Preferences...
+qt-accel-open-addons-folder = &Open Add-ons Folder
+qt-accel-preferences = &Preferences
qt-accel-previous-card = &Previous Card
qt-accel-select-all = Select &All
qt-accel-select-notes = Select &Notes
-qt-accel-support-anki = &Support Anki...
+qt-accel-support-anki = &Support Anki
qt-accel-switch-profile = &Switch Profile
qt-accel-tools = &Tools
qt-accel-undo = &Undo
diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py
index 68e1e97af..07b4d2e74 100644
--- a/qt/aqt/addcards.py
+++ b/qt/aqt/addcards.py
@@ -54,6 +54,20 @@ class AddCards(QMainWindow):
gui_hooks.add_cards_did_init(self)
self.show()
+ def set_note(self, note: Note) -> None:
+ """Set tags, field contents and notetype (and its deck)
+ according to `note`.
+ """
+ self.notetype_chooser.selected_notetype_id = note.mid
+ if deck_id := self.col.default_deck_for_notetype(note.mid):
+ self.deck_chooser.selected_deck_id = deck_id
+
+ new_note = self._new_note()
+ new_note.fields = note.fields
+ new_note.tags = note.tags
+
+ self.setAndFocusNote(new_note)
+
def setupEditor(self) -> None:
self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self, True)
self.editor.web.eval("noteEditorPromise.then(() => activateStickyShortcuts());")
diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py
index 1a02ce220..9eda5ed52 100644
--- a/qt/aqt/browser/browser.py
+++ b/qt/aqt/browser/browser.py
@@ -40,6 +40,7 @@ from aqt.undo import UndoActionsInfo
from aqt.utils import (
HelpPage,
KeyboardModifiersPressed,
+ add_ellipsis_to_action_label,
current_window,
ensure_editor_saved,
getTag,
@@ -183,6 +184,7 @@ class Browser(QMainWindow):
f.actionCreateFilteredDeck.setShortcuts(["Ctrl+G", "Ctrl+Alt+G"])
# notes
qconnect(f.actionAdd.triggered, self.mw.onAddCard)
+ qconnect(f.actionCopy.triggered, self.on_create_copy)
qconnect(f.actionAdd_Tags.triggered, self.add_tags_to_selected_notes)
qconnect(f.actionRemove_Tags.triggered, self.remove_tags_from_selected_notes)
qconnect(f.actionClear_Unused_Tags.triggered, self.clear_unused_tags)
@@ -229,6 +231,8 @@ class Browser(QMainWindow):
gui_hooks.browser_menus_did_init(self)
self.mw.maybeHideAccelerators(self)
+ add_ellipsis_to_action_label(f.actionCopy)
+
def closeEvent(self, evt: QCloseEvent) -> None:
if self._closeEventHasCleanedUp:
evt.accept()
@@ -479,6 +483,7 @@ class Browser(QMainWindow):
self._update_flags_menu()
self._update_toggle_mark_action()
self._update_toggle_suspend_action()
+ self.form.actionCopy.setEnabled(self.table.has_current())
self.form.action_Info.setEnabled(self.table.has_current())
self.form.actionPreviousCard.setEnabled(self.table.has_previous())
self.form.actionNextCard.setEnabled(self.table.has_next())
@@ -582,6 +587,10 @@ class Browser(QMainWindow):
# Misc menu options
######################################################################
+ def on_create_copy(self) -> None:
+ if note := self.table.get_current_note():
+ aqt.dialogs.open("AddCards", self.mw).set_note(note)
+
@no_arg_trigger
@skip_if_selection_is_empty
@ensure_editor_saved
diff --git a/qt/aqt/forms/browser.ui b/qt/aqt/forms/browser.ui
index 0012b346b..00bb16d03 100644
--- a/qt/aqt/forms/browser.ui
+++ b/qt/aqt/forms/browser.ui
@@ -17,7 +17,7 @@
-
+
:/icons/anki.png:/icons/anki.png
@@ -144,12 +144,12 @@
false
-
- false
-
20
+
+ false
+
true
@@ -209,7 +209,7 @@
0
0
750
- 21
+ 22
diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py
index f64f72f21..409177b8c 100644
--- a/qt/aqt/utils.py
+++ b/qt/aqt/utils.py
@@ -847,6 +847,17 @@ def qtMenuShortcutWorkaround(qmenu: QMenu) -> None:
######################################################################
+def add_ellipsis_to_action_label(*actions: QAction) -> None:
+ """Pass actions to add '...' to their labels, indicating that more input is
+ required before they can be performed.
+
+ This approach is used so that the same fluent translations can be used on
+ mobile, where the '...' convention does not exist.
+ """
+ for action in actions:
+ action.setText(tr.actions_with_ellipsis(action=action.text()))
+
+
def supportText() -> str:
import platform
import time