diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java
index efec6542ab0..ff5baf2606d 100644
--- a/src/main/java/org/jabref/gui/BasePanel.java
+++ b/src/main/java/org/jabref/gui/BasePanel.java
@@ -1,17 +1,12 @@
 package org.jabref.gui;
 
-import java.io.IOException;
-import java.io.StringReader;
 import java.nio.file.Path;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
 
 import javax.swing.undo.CannotRedoException;
 import javax.swing.undo.CannotUndoException;
@@ -35,14 +30,10 @@
 import org.jabref.gui.cleanup.CleanupAction;
 import org.jabref.gui.collab.DatabaseChangeMonitor;
 import org.jabref.gui.collab.DatabaseChangePane;
-import org.jabref.gui.desktop.JabRefDesktop;
-import org.jabref.gui.edit.CopyBibTeXKeyAndLinkAction;
 import org.jabref.gui.edit.ReplaceStringAction;
 import org.jabref.gui.entryeditor.EntryEditor;
 import org.jabref.gui.exporter.SaveDatabaseAction;
-import org.jabref.gui.exporter.WriteXMPAction;
 import org.jabref.gui.externalfiles.DownloadFullTextAction;
-import org.jabref.gui.externalfiletype.ExternalFileType;
 import org.jabref.gui.externalfiletype.ExternalFileTypes;
 import org.jabref.gui.importer.actions.AppendDatabaseAction;
 import org.jabref.gui.journals.AbbreviateAction;
@@ -50,30 +41,18 @@
 import org.jabref.gui.journals.UnabbreviateAction;
 import org.jabref.gui.maintable.MainTable;
 import org.jabref.gui.maintable.MainTableDataModel;
-import org.jabref.gui.mergeentries.MergeEntriesAction;
-import org.jabref.gui.mergeentries.MergeWithFetchedEntryAction;
-import org.jabref.gui.preview.CitationStyleToClipboardWorker;
 import org.jabref.gui.specialfields.SpecialFieldDatabaseChangeListener;
-import org.jabref.gui.specialfields.SpecialFieldValueViewModel;
-import org.jabref.gui.specialfields.SpecialFieldViewModel;
 import org.jabref.gui.undo.CountingUndoManager;
 import org.jabref.gui.undo.NamedCompound;
 import org.jabref.gui.undo.UndoableFieldChange;
 import org.jabref.gui.undo.UndoableInsertEntries;
 import org.jabref.gui.undo.UndoableRemoveEntries;
 import org.jabref.gui.util.DefaultTaskExecutor;
-import org.jabref.gui.worker.SendAsEMailAction;
 import org.jabref.logic.citationstyle.CitationStyleCache;
-import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
 import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.layout.Layout;
-import org.jabref.logic.layout.LayoutHelper;
 import org.jabref.logic.pdf.FileAnnotationCache;
 import org.jabref.logic.search.SearchQuery;
 import org.jabref.logic.util.UpdateField;
-import org.jabref.logic.util.io.FileFinder;
-import org.jabref.logic.util.io.FileFinders;
-import org.jabref.logic.util.io.FileUtil;
 import org.jabref.model.FieldChange;
 import org.jabref.model.database.BibDatabase;
 import org.jabref.model.database.BibDatabaseContext;
@@ -85,15 +64,10 @@
 import org.jabref.model.database.shared.DatabaseLocation;
 import org.jabref.model.database.shared.DatabaseSynchronizer;
 import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.FileFieldParser;
-import org.jabref.model.entry.LinkedFile;
 import org.jabref.model.entry.event.EntriesEventSource;
 import org.jabref.model.entry.event.EntryChangedEvent;
 import org.jabref.model.entry.field.Field;
 import org.jabref.model.entry.field.FieldFactory;
-import org.jabref.model.entry.field.SpecialField;
-import org.jabref.model.entry.field.SpecialFieldValue;
-import org.jabref.model.entry.field.StandardField;
 import org.jabref.preferences.JabRefPreferences;
 
 import com.google.common.eventbus.Subscribe;
@@ -259,21 +233,6 @@ private void setupActions() {
 
         actions.put(Actions.SAVE_SELECTED_AS_PLAIN, saveAction::saveSelectedAsPlain);
 
-        // The action for copying selected entries.
-        actions.put(Actions.COPY, this::copy);
-
-        actions.put(Actions.CUT, this::cut);
-
-        actions.put(Actions.DELETE, () -> delete(false));
-
-        // The action for pasting entries or cell contents.
-        //  - more robust detection of available content flavors (doesn't only look at first one offered)
-        //  - support for parsing string-flavor clipboard contents which are bibtex entries.
-        //    This allows you to (a) paste entire bibtex entries from a text editor, web browser, etc
-        //                       (b) copy and paste entries between multiple instances of JabRef (since
-        //         only the text representation seems to get as far as the X clipboard, at least on my system)
-        actions.put(Actions.PASTE, this::paste);
-
         actions.put(Actions.SELECT_ALL, mainTable.getSelectionModel()::selectAll);
 
         // The action for auto-generating keys.
@@ -282,90 +241,15 @@ private void setupActions() {
         // The action for cleaning up entry.
         actions.put(Actions.CLEANUP, cleanUpAction);
 
-        actions.put(Actions.MERGE_ENTRIES, () -> new MergeEntriesAction(frame, Globals.stateManager).execute());
-
-        // The action for copying the selected entry's key.
-        actions.put(Actions.COPY_KEY, this::copyKey);
-
-        // The action for copying the selected entry's title.
-        actions.put(Actions.COPY_TITLE, this::copyTitle);
-
-        // The action for copying a cite for the selected entry.
-        actions.put(Actions.COPY_CITE_KEY, this::copyCiteKey);
-
-        // The action for copying the BibTeX key and the title for the first selected entry
-        actions.put(Actions.COPY_KEY_AND_TITLE, this::copyKeyAndTitle);
-
-        actions.put(Actions.COPY_CITATION_ASCII_DOC, () -> copyCitationToClipboard(CitationStyleOutputFormat.ASCII_DOC));
-        actions.put(Actions.COPY_CITATION_XSLFO, () -> copyCitationToClipboard(CitationStyleOutputFormat.XSL_FO));
-        actions.put(Actions.COPY_CITATION_HTML, () -> copyCitationToClipboard(CitationStyleOutputFormat.HTML));
-        actions.put(Actions.COPY_CITATION_RTF, () -> copyCitationToClipboard(CitationStyleOutputFormat.RTF));
-        actions.put(Actions.COPY_CITATION_TEXT, () -> copyCitationToClipboard(CitationStyleOutputFormat.TEXT));
-
-        // The action for copying the BibTeX keys as hyperlinks to the urls of the selected entries
-        actions.put(Actions.COPY_KEY_AND_LINK, new CopyBibTeXKeyAndLinkAction(mainTable, Globals.clipboardManager));
-
         actions.put(Actions.MERGE_DATABASE, new AppendDatabaseAction(frame, this));
 
-        actions.put(Actions.OPEN_EXTERNAL_FILE, this::openExternalFile);
-
-        actions.put(Actions.OPEN_FOLDER, () -> JabRefExecutorService.INSTANCE.execute(() -> {
-            final List<Path> files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()));
-            for (final Path f : files) {
-                try {
-                    JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath());
-                } catch (IOException e) {
-                    LOGGER.info("Could not open folder", e);
-                }
-            }
-        }));
-
-        actions.put(Actions.OPEN_CONSOLE, () -> JabRefDesktop.openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null)));
-
         actions.put(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, () -> {
             DatabaseSynchronizer dbmsSynchronizer = frame.getCurrentBasePanel().getBibDatabaseContext().getDBMSSynchronizer();
             dbmsSynchronizer.pullChanges();
         });
 
-        actions.put(Actions.OPEN_URL, new OpenURLAction());
-
-        actions.put(Actions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(this, frame.getDialogService()));
-
         actions.put(Actions.REPLACE_ALL, () -> (new ReplaceStringAction(this)).execute());
 
-        actions.put(new SpecialFieldValueViewModel(SpecialField.RELEVANCE.getValues().get(0)).getCommand(),
-                new SpecialFieldViewModel(SpecialField.RELEVANCE, undoManager).getSpecialFieldAction(SpecialField.RELEVANCE.getValues().get(0), frame));
-
-        actions.put(new SpecialFieldValueViewModel(SpecialField.QUALITY.getValues().get(0)).getCommand(),
-                new SpecialFieldViewModel(SpecialField.QUALITY, undoManager).getSpecialFieldAction(SpecialField.QUALITY.getValues().get(0), frame));
-
-        actions.put(new SpecialFieldValueViewModel(SpecialField.PRINTED.getValues().get(0)).getCommand(),
-                new SpecialFieldViewModel(SpecialField.PRINTED, undoManager).getSpecialFieldAction(SpecialField.PRINTED.getValues().get(0), frame));
-
-        for (SpecialFieldValue prio : SpecialField.PRIORITY.getValues()) {
-            actions.put(new SpecialFieldValueViewModel(prio).getCommand(),
-                    new SpecialFieldViewModel(SpecialField.PRIORITY, undoManager).getSpecialFieldAction(prio, this.frame));
-        }
-        for (SpecialFieldValue rank : SpecialField.RANKING.getValues()) {
-            actions.put(new SpecialFieldValueViewModel(rank).getCommand(),
-                    new SpecialFieldViewModel(SpecialField.RANKING, undoManager).getSpecialFieldAction(rank, this.frame));
-        }
-        for (SpecialFieldValue status : SpecialField.READ_STATUS.getValues()) {
-            actions.put(new SpecialFieldValueViewModel(status).getCommand(),
-                    new SpecialFieldViewModel(SpecialField.READ_STATUS, undoManager).getSpecialFieldAction(status, this.frame));
-        }
-
-        actions.put(Actions.NEXT_PREVIEW_STYLE, () -> {
-            entryEditor.nextPreviewStyle();
-        });
-        actions.put(Actions.PREVIOUS_PREVIEW_STYLE, () -> {
-            entryEditor.previousPreviewStyle();
-        });
-
-        actions.put(Actions.SEND_AS_EMAIL, new SendAsEMailAction(frame));
-
-        actions.put(Actions.WRITE_XMP, new WriteXMPAction(this)::execute);
-
         actions.put(Actions.ABBREVIATE_DEFAULT, new AbbreviateAction(this, AbbreviationType.DEFAULT));
         actions.put(Actions.ABBREVIATE_MEDLINE, new AbbreviateAction(this, AbbreviationType.MEDLINE));
         actions.put(Actions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(this, AbbreviationType.SHORTEST_UNIQUE));
@@ -374,16 +258,6 @@ private void setupActions() {
         actions.put(Actions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(this)::execute);
     }
 
-    /**
-     * Generates and copies citations based on the selected entries to the clipboard
-     *
-     * @param outputFormat the desired {@link CitationStyleOutputFormat}
-     */
-    private void copyCitationToClipboard(CitationStyleOutputFormat outputFormat) {
-        CitationStyleToClipboardWorker worker = new CitationStyleToClipboardWorker(this, outputFormat, dialogService, Globals.clipboardManager, Globals.prefs.getPreviewPreferences());
-        worker.copyCitationStyleToClipboard(Globals.TASK_EXECUTOR);
-    }
-
     /**
      * Removes the selected entries from the database
      *
@@ -423,157 +297,6 @@ public void delete(BibEntry entry) {
         delete(false, Collections.singletonList(entry));
     }
 
-    private void copyTitle() {
-        List<BibEntry> selectedBibEntries = mainTable.getSelectedEntries();
-        if (!selectedBibEntries.isEmpty()) {
-            // Collect all non-null titles.
-            List<String> titles = selectedBibEntries.stream()
-                                                    .filter(bibEntry -> bibEntry.getTitle().isPresent())
-                                                    .map(bibEntry -> bibEntry.getTitle().get())
-                                                    .collect(Collectors.toList());
-
-            if (titles.isEmpty()) {
-                output(Localization.lang("None of the selected entries have titles."));
-                return;
-            }
-            final String copiedTitles = String.join("\n", titles);
-            Globals.clipboardManager.setContent(copiedTitles);
-
-            if (titles.size() == selectedBibEntries.size()) {
-                // All entries had titles.
-                output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedTitles) + "'.");
-            } else {
-                output(Localization.lang("Warning: %0 out of %1 entries have undefined title.", Integer.toString(selectedBibEntries.size() - titles.size()), Integer.toString(selectedBibEntries.size())));
-            }
-        }
-    }
-
-    private void copyCiteKey() {
-        List<BibEntry> bes = mainTable.getSelectedEntries();
-        if (!bes.isEmpty()) {
-            List<String> keys = new ArrayList<>(bes.size());
-            // Collect all non-null keys.
-            for (BibEntry be : bes) {
-                be.getCiteKeyOptional().ifPresent(keys::add);
-            }
-            if (keys.isEmpty()) {
-                output(Localization.lang("None of the selected entries have BibTeX keys."));
-                return;
-            }
-
-            String citeCommand = Optional.ofNullable(Globals.prefs.get(JabRefPreferences.CITE_COMMAND))
-                                         .filter(cite -> cite.contains("\\")) // must contain \
-                                         .orElse("\\cite");
-            final String copiedCiteCommand = citeCommand + "{" + String.join(",", keys) + '}';
-            Globals.clipboardManager.setContent(copiedCiteCommand);
-
-            if (keys.size() == bes.size()) {
-                // All entries had keys.
-                output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedCiteCommand) + "'.");
-            } else {
-                output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size())));
-            }
-        }
-    }
-
-    private void copyKey() {
-        List<BibEntry> bes = mainTable.getSelectedEntries();
-        if (!bes.isEmpty()) {
-            List<String> keys = new ArrayList<>(bes.size());
-            // Collect all non-null keys.
-            for (BibEntry be : bes) {
-                be.getCiteKeyOptional().ifPresent(keys::add);
-            }
-            if (keys.isEmpty()) {
-                output(Localization.lang("None of the selected entries have BibTeX keys."));
-                return;
-            }
-
-            final String copiedKeys = String.join(",", keys);
-            Globals.clipboardManager.setContent(copiedKeys);
-
-            if (keys.size() == bes.size()) {
-                // All entries had keys.
-                output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedKeys) + "'.");
-            } else {
-                output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size())));
-            }
-        }
-    }
-
-    private void copyKeyAndTitle() {
-        List<BibEntry> bes = mainTable.getSelectedEntries();
-        if (!bes.isEmpty()) {
-            // OK: in a future version, this string should be configurable to allow arbitrary exports
-            StringReader sr = new StringReader("\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n");
-            Layout layout;
-            try {
-                layout = new LayoutHelper(sr, Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader))
-                        .getLayoutFromText();
-            } catch (IOException e) {
-                LOGGER.info("Could not get layout", e);
-                return;
-            }
-
-            StringBuilder sb = new StringBuilder();
-
-            int copied = 0;
-            // Collect all non-null keys.
-            for (BibEntry be : bes) {
-                if (be.hasCiteKey()) {
-                    copied++;
-                    sb.append(layout.doLayout(be, bibDatabaseContext.getDatabase()));
-                }
-            }
-
-            if (copied == 0) {
-                output(Localization.lang("None of the selected entries have BibTeX keys."));
-                return;
-            }
-
-            final String copiedKeysAndTitles = sb.toString();
-            Globals.clipboardManager.setContent(copiedKeysAndTitles);
-
-            if (copied == bes.size()) {
-                // All entries had keys.
-                output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedKeysAndTitles) + "'.");
-            } else {
-                output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - copied), Integer.toString(bes.size())));
-            }
-        }
-    }
-
-    private void openExternalFile() {
-        final List<BibEntry> selectedEntries = mainTable.getSelectedEntries();
-        if (selectedEntries.size() != 1) {
-            output(Localization.lang("This operation requires exactly one item to be selected."));
-            return;
-        }
-        JabRefExecutorService.INSTANCE.execute(() -> {
-            final BibEntry entry = selectedEntries.get(0);
-            if (!entry.hasField(StandardField.FILE)) {
-                // no bibtex field
-                new SearchAndOpenFile(entry, BasePanel.this).searchAndOpen();
-                return;
-            }
-
-            List<LinkedFile> files = new ArrayList<>();
-            entry.getField(StandardField.FILE).map(FileFieldParser::parse).ifPresent(files::addAll);
-
-            if (files.isEmpty()) {
-                // content in BibTeX field is not readable
-                new SearchAndOpenFile(entry, BasePanel.this).searchAndOpen();
-                return;
-            }
-            LinkedFile flEntry = files.get(0);
-            try {
-                JabRefDesktop.openExternalFileAnyFormat(this.getBibDatabaseContext(), flEntry.getLink(), ExternalFileTypes.getInstance().fromLinkedFile(flEntry, true));
-            } catch (IOException ex) {
-                dialogService.showErrorDialogAndWait(ex);
-            }
-        });
-    }
-
     /**
      * This method is called from JabRefFrame if a database specific action is requested by the user. Runs the command
      * if it is defined, or prints an error message to the standard error stream.
@@ -1077,47 +800,6 @@ public void cut() {
         mainTable.cut();
     }
 
-    private static class SearchAndOpenFile {
-
-        private final BibEntry entry;
-        private final BasePanel basePanel;
-
-        public SearchAndOpenFile(final BibEntry entry, final BasePanel basePanel) {
-            this.entry = entry;
-            this.basePanel = basePanel;
-        }
-
-        public void searchAndOpen() {
-            if (!Globals.prefs.getBoolean(JabRefPreferences.RUN_AUTOMATIC_FILE_SEARCH)) {
-                /*  The search can lead to an unexpected 100% CPU usage which is perceived
-                    as a bug, if the search incidentally starts at a directory with lots
-                    of stuff below. It is now disabled by default. */
-                return;
-            }
-
-            final Set<ExternalFileType> types = ExternalFileTypes.getInstance().getExternalFileTypeSelection();
-            final List<Path> dirs = basePanel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences());
-            final List<String> extensions = types.stream().map(ExternalFileType::getExtension).collect(Collectors.toList());
-
-            // Run the search operation:
-            FileFinder fileFinder = FileFinders.constructFromConfiguration(Globals.prefs.getAutoLinkPreferences());
-            try {
-                List<Path> files = fileFinder.findAssociatedFiles(entry, dirs, extensions);
-                if (!files.isEmpty()) {
-                    Path file = files.get(0);
-                    Optional<ExternalFileType> type = ExternalFileTypes.getInstance().getExternalFileTypeByFile(file);
-                    if (type.isPresent()) {
-                        JabRefDesktop.openExternalFileAnyFormat(file, basePanel.getBibDatabaseContext(), type);
-                        basePanel.output(Localization.lang("External viewer called") + '.');
-                    }
-                }
-            } catch (IOException ex) {
-                LOGGER.error("Problems with finding/or opening files ", ex);
-                basePanel.output(Localization.lang("Error") + ": " + ex.getMessage());
-            }
-        }
-    }
-
     private class GroupTreeListener {
 
         @Subscribe
@@ -1200,60 +882,6 @@ public void action() {
         }
     }
 
-    private class OpenURLAction implements BaseAction {
-
-        @Override
-        public void action() {
-            final List<BibEntry> bes = mainTable.getSelectedEntries();
-            if (bes.size() == 1) {
-                Field field = StandardField.DOI;
-                Optional<String> link = bes.get(0).getField(StandardField.DOI);
-                if (bes.get(0).hasField(StandardField.URL)) {
-                    link = bes.get(0).getField(StandardField.URL);
-                    field = StandardField.URL;
-                }
-                if (link.isPresent()) {
-                    try {
-                        JabRefDesktop.openExternalViewer(bibDatabaseContext, link.get(), field);
-                        output(Localization.lang("External viewer called") + '.');
-                    } catch (IOException ex) {
-                        output(Localization.lang("Error") + ": " + ex.getMessage());
-                    }
-                } else {
-                    // No URL or DOI found in the "url" and "doi" fields.
-                    // Look for web links in the "file" field as a fallback:
-
-                    List<LinkedFile> files = bes.get(0).getFiles();
-
-                    Optional<LinkedFile> linkedFile = files.stream()
-                                                           .filter(file -> (StandardField.URL.getName().equalsIgnoreCase(file.getFileType())
-                                                                   || StandardField.PS.getName().equalsIgnoreCase(file.getFileType())
-                                                                   || StandardField.PDF.getName().equalsIgnoreCase(file.getFileType())))
-                                                           .findFirst();
-
-                    if (linkedFile.isPresent()) {
-
-                        try {
-
-                            JabRefDesktop.openExternalFileAnyFormat(bibDatabaseContext,
-                                    linkedFile.get().getLink(),
-                                    ExternalFileTypes.getInstance().fromLinkedFile(linkedFile.get(), true));
-
-                            output(Localization.lang("External viewer called") + '.');
-                        } catch (IOException e) {
-                            output(Localization.lang("Could not open link"));
-                            LOGGER.info("Could not open link", e);
-                        }
-                    } else {
-                        output(Localization.lang("No URL defined") + '.');
-                    }
-                }
-            } else {
-                output(Localization.lang("This operation requires exactly one item to be selected."));
-            }
-        }
-    }
-
     private class RedoAction implements BaseAction {
 
         @Override
diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java
index c7bc0a63088..8ef016891a6 100644
--- a/src/main/java/org/jabref/gui/JabRefFrame.java
+++ b/src/main/java/org/jabref/gui/JabRefFrame.java
@@ -30,7 +30,6 @@
 import javafx.scene.control.SplitPane;
 import javafx.scene.control.Tab;
 import javafx.scene.control.TabPane;
-import javafx.scene.control.TextInputControl;
 import javafx.scene.control.ToolBar;
 import javafx.scene.control.Tooltip;
 import javafx.scene.control.skin.TabPaneSkin;
@@ -60,14 +59,18 @@
 import org.jabref.gui.dialogs.AutosaveUIManager;
 import org.jabref.gui.documentviewer.ShowDocumentViewerAction;
 import org.jabref.gui.duplicationFinder.DuplicateSearch;
+import org.jabref.gui.edit.CopyMoreAction;
+import org.jabref.gui.edit.EditAction;
 import org.jabref.gui.edit.ManageKeywordsAction;
 import org.jabref.gui.edit.MassSetFieldsAction;
 import org.jabref.gui.edit.OpenBrowserAction;
+import org.jabref.gui.entryeditor.PreviewSwitchAction;
 import org.jabref.gui.exporter.ExportCommand;
 import org.jabref.gui.exporter.ExportToClipboardAction;
 import org.jabref.gui.exporter.ManageCustomExportsAction;
 import org.jabref.gui.exporter.SaveAllAction;
 import org.jabref.gui.exporter.SaveDatabaseAction;
+import org.jabref.gui.exporter.WriteXMPAction;
 import org.jabref.gui.externalfiles.AutoLinkFilesAction;
 import org.jabref.gui.externalfiles.FindUnlinkedFilesAction;
 import org.jabref.gui.externalfiletype.EditExternalFileTypesAction;
@@ -89,9 +92,11 @@
 import org.jabref.gui.keyboard.KeyBinding;
 import org.jabref.gui.libraryproperties.LibraryPropertiesAction;
 import org.jabref.gui.menus.FileHistoryMenu;
+import org.jabref.gui.mergeentries.MergeEntriesAction;
 import org.jabref.gui.metadata.BibtexStringEditorAction;
 import org.jabref.gui.metadata.PreambleEditor;
 import org.jabref.gui.preferences.ShowPreferencesAction;
+import org.jabref.gui.preview.CopyCitationAction;
 import org.jabref.gui.protectedterms.ManageProtectedTermsAction;
 import org.jabref.gui.push.PushToApplicationAction;
 import org.jabref.gui.push.PushToApplicationsManager;
@@ -104,6 +109,7 @@
 import org.jabref.gui.util.DefaultTaskExecutor;
 import org.jabref.logic.autosaveandbackup.AutosaveManager;
 import org.jabref.logic.autosaveandbackup.BackupManager;
+import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
 import org.jabref.logic.importer.IdFetcher;
 import org.jabref.logic.importer.ParserResult;
 import org.jabref.logic.importer.WebFetchers;
@@ -481,13 +487,13 @@ private Node createToolbar() {
                 factory.createIconButton(StandardActions.NEW_ARTICLE, new NewEntryAction(this, StandardEntryType.Article, dialogService, Globals.prefs, stateManager)),
                 factory.createIconButton(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs, stateManager)),
                 factory.createIconButton(StandardActions.NEW_ENTRY_FROM_PLAIN_TEXT, new ExtractBibtexAction(stateManager)),
-                factory.createIconButton(StandardActions.DELETE_ENTRY, new OldDatabaseCommandWrapper(Actions.DELETE, this, stateManager)),
+                factory.createIconButton(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, this, stateManager)),
                 new Separator(Orientation.VERTICAL),
                 factory.createIconButton(StandardActions.UNDO, new OldDatabaseCommandWrapper(Actions.UNDO, this, stateManager)),
                 factory.createIconButton(StandardActions.REDO, new OldDatabaseCommandWrapper(Actions.REDO, this, stateManager)),
-                factory.createIconButton(StandardActions.CUT, new OldDatabaseCommandWrapper(Actions.CUT, this, stateManager)),
-                factory.createIconButton(StandardActions.COPY, new OldDatabaseCommandWrapper(Actions.COPY, this, stateManager)),
-                factory.createIconButton(StandardActions.PASTE, new OldDatabaseCommandWrapper(Actions.PASTE, this, stateManager)),
+                factory.createIconButton(StandardActions.CUT, new EditAction(StandardActions.CUT,this, stateManager)),
+                factory.createIconButton(StandardActions.COPY, new EditAction(StandardActions.COPY,this, stateManager)),
+                factory.createIconButton(StandardActions.PASTE, new EditAction(StandardActions.PASTE,this, stateManager)),
                 new Separator(Orientation.VERTICAL),
                 pushToApplicationButton,
                 factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new OldDatabaseCommandWrapper(Actions.MAKE_KEY, this, stateManager)),
@@ -571,6 +577,9 @@ public void init() {
                     }
                 });
 
+        // Wait for the scene to be created, otherwise focusOwnerProperty is not provided
+        Platform.runLater(() -> stateManager.focusOwnerProperty().bind(
+                EasyBind.map(mainStage.getScene().focusOwnerProperty(), Optional::ofNullable)));
         /*
          * The following state listener makes sure focus is registered with the
          * correct database when the user switches tabs. Without this,
@@ -700,19 +709,19 @@ private MenuBar createMenu() {
 
                 new SeparatorMenuItem(),
 
-                factory.createMenuItem(StandardActions.CUT, new EditAction(Actions.CUT)),
+                factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, this, stateManager)),
 
-                factory.createMenuItem(StandardActions.COPY, new EditAction(Actions.COPY)),
+                factory.createMenuItem(StandardActions.COPY, new EditAction(StandardActions.COPY, this, stateManager)),
                 factory.createSubMenu(StandardActions.COPY_MORE,
-                        factory.createMenuItem(StandardActions.COPY_TITLE, new OldDatabaseCommandWrapper(Actions.COPY_TITLE, this, stateManager)),
-                        factory.createMenuItem(StandardActions.COPY_KEY, new OldDatabaseCommandWrapper(Actions.COPY_KEY, this, stateManager)),
-                        factory.createMenuItem(StandardActions.COPY_CITE_KEY, new OldDatabaseCommandWrapper(Actions.COPY_CITE_KEY, this, stateManager)),
-                        factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new OldDatabaseCommandWrapper(Actions.COPY_KEY_AND_TITLE, this, stateManager)),
-                        factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new OldDatabaseCommandWrapper(Actions.COPY_KEY_AND_LINK, this, stateManager)),
-                        factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new OldDatabaseCommandWrapper(Actions.COPY_CITATION_HTML, this, stateManager)),
+                        factory.createMenuItem(StandardActions.COPY_TITLE, new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, Globals.clipboardManager, prefs)),
+                        factory.createMenuItem(StandardActions.COPY_KEY, new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, Globals.clipboardManager, prefs)),
+                        factory.createMenuItem(StandardActions.COPY_CITE_KEY, new CopyMoreAction(StandardActions.COPY_CITE_KEY, dialogService, stateManager, Globals.clipboardManager, prefs)),
+                        factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new CopyMoreAction(StandardActions.COPY_KEY_AND_TITLE, dialogService, stateManager, Globals.clipboardManager, prefs)),
+                        factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new CopyMoreAction(StandardActions.COPY_KEY_AND_LINK, dialogService, stateManager, Globals.clipboardManager, prefs)),
+                        factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.clipboardManager, prefs.getPreviewPreferences())),
                         factory.createMenuItem(StandardActions.EXPORT_SELECTED_TO_CLIPBOARD, new ExportToClipboardAction(this, dialogService))),
 
-                factory.createMenuItem(StandardActions.PASTE, new EditAction(Actions.PASTE)),
+                factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, this, stateManager)),
 
                 new SeparatorMenuItem(),
 
@@ -727,12 +736,14 @@ private MenuBar createMenu() {
         if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) {
             edit.getItems().addAll(
                     new SeparatorMenuItem(),
-                    SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.RANKING, factory, undoManager),
-                    SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.RELEVANCE, factory),
-                    SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.QUALITY, factory),
-                    SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.PRINTED, factory),
-                    SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.PRIORITY, factory, undoManager),
-                    SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.READ_STATUS, factory, undoManager)
+                    // ToDo: SpecialField needs the active BasePanel to mark it as changed.
+                    //  Refactor BasePanel, should mark the BibDatabaseContext or the UndoManager as dirty instead!
+                    SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, this, dialogService, stateManager),
+                    SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, this, dialogService, stateManager),
+                    SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, this, dialogService, stateManager),
+                    SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, this, dialogService, stateManager),
+                    SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, this, dialogService, stateManager),
+                    SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, this, dialogService, stateManager)
             );
         }
 
@@ -740,7 +751,7 @@ private MenuBar createMenu() {
         library.getItems().addAll(
                 factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs, stateManager)),
                 factory.createMenuItem(StandardActions.NEW_ENTRY_FROM_PLAIN_TEXT, new ExtractBibtexAction(stateManager)),
-                factory.createMenuItem(StandardActions.DELETE_ENTRY, new OldDatabaseCommandWrapper(Actions.DELETE, this, stateManager)),
+                factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, this, stateManager)),
 
                 new SeparatorMenuItem(),
 
@@ -759,6 +770,7 @@ private MenuBar createMenu() {
 
         quality.getItems().addAll(
                 factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this, dialogService, stateManager)),
+                factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(this, dialogService, stateManager)),
                 factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(this, stateManager, Globals.TASK_EXECUTOR)),
                 factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new OldDatabaseCommandWrapper(Actions.CLEANUP, this, stateManager)),
 
@@ -792,7 +804,7 @@ private MenuBar createMenu() {
                 new SeparatorMenuItem(),
 
                 factory.createMenuItem(StandardActions.FIND_UNLINKED_FILES, new FindUnlinkedFilesAction(this, stateManager)),
-                factory.createMenuItem(StandardActions.WRITE_XMP, new OldDatabaseCommandWrapper(Actions.WRITE_XMP, this, stateManager)),
+                factory.createMenuItem(StandardActions.WRITE_XMP, new WriteXMPAction(stateManager, dialogService)),
                 factory.createMenuItem(StandardActions.COPY_LINKED_FILES, new CopyFilesAction(stateManager, this.getDialogService())),
 
                 new SeparatorMenuItem(),
@@ -802,7 +814,7 @@ private MenuBar createMenu() {
 
                 new SeparatorMenuItem(),
 
-                factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new OldDatabaseCommandWrapper(Actions.SEND_AS_EMAIL, this, stateManager)),
+                factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsEMailAction(dialogService, stateManager)),
                 pushToApplicationMenuItem
         );
 
@@ -820,14 +832,14 @@ private MenuBar createMenu() {
 
                     new SeparatorMenuItem(),
 
-                    factory.createMenuItem(StandardActions.NEXT_PREVIEW_STYLE, new OldDatabaseCommandWrapper(Actions.NEXT_PREVIEW_STYLE, this, stateManager)),
-                    factory.createMenuItem(StandardActions.PREVIOUS_PREVIEW_STYLE, new OldDatabaseCommandWrapper(Actions.PREVIOUS_PREVIEW_STYLE, this, stateManager)),
+                    factory.createMenuItem(StandardActions.NEXT_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.NEXT, this, stateManager)),
+                    factory.createMenuItem(StandardActions.PREVIOUS_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.PREVIOUS, this, stateManager)),
 
                     new SeparatorMenuItem(),
 
                     factory.createMenuItem(StandardActions.SHOW_PDF_VIEWER, new ShowDocumentViewerAction()),
                     factory.createMenuItem(StandardActions.EDIT_ENTRY, new OldDatabaseCommandWrapper(Actions.EDIT, this, stateManager)),
-                    factory.createMenuItem(StandardActions.OPEN_CONSOLE, new OldDatabaseCommandWrapper(Actions.OPEN_CONSOLE, this, stateManager))
+                    factory.createMenuItem(StandardActions.OPEN_CONSOLE, new OpenConsoleAction(stateManager))
             );
         });
 
@@ -1246,63 +1258,6 @@ public void execute() {
         }
     }
 
-    /**
-     * Class for handling general actions; cut, copy and paste. The focused component is kept track of by
-     * Globals.focusListener, and we call the action stored under the relevant name in its action map.
-     */
-    private class EditAction extends SimpleCommand {
-
-        private final Actions command;
-
-        public EditAction(Actions command) {
-            this.command = command;
-        }
-
-        @Override
-        public String toString() {
-            return this.command.toString();
-        }
-
-        @Override
-        public void execute() {
-            Node focusOwner = mainStage.getScene().getFocusOwner();
-            if (focusOwner != null) {
-                if (focusOwner instanceof TextInputControl) {
-                    // Focus is on text field -> copy/paste/cut selected text
-                    TextInputControl textInput = (TextInputControl) focusOwner;
-                    switch (command) {
-                        case COPY:
-                            textInput.copy();
-                            break;
-                        case CUT:
-                            textInput.cut();
-                            break;
-                        case PASTE:
-                            // handled by FX in TextInputControl#paste
-                            break;
-                        default:
-                            throw new IllegalStateException("Only cut/copy/paste supported but got " + command);
-                    }
-                } else {
-                    // Not sure what is selected -> copy/paste/cut selected entries
-                    switch (command) {
-                        case COPY:
-                            getCurrentBasePanel().copy();
-                            break;
-                        case CUT:
-                            getCurrentBasePanel().cut();
-                            break;
-                        case PASTE:
-                            // handled by FX in TextInputControl#paste
-                            break;
-                        default:
-                            throw new IllegalStateException("Only cut/copy/paste supported but got " + command);
-                    }
-                }
-            }
-        }
-    }
-
     private class CloseDatabaseAction extends SimpleCommand {
 
         @Override
diff --git a/src/main/java/org/jabref/gui/OpenConsoleAction.java b/src/main/java/org/jabref/gui/OpenConsoleAction.java
new file mode 100644
index 00000000000..ce634dd73e5
--- /dev/null
+++ b/src/main/java/org/jabref/gui/OpenConsoleAction.java
@@ -0,0 +1,34 @@
+package org.jabref.gui;
+
+import java.io.IOException;
+
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.desktop.JabRefDesktop;
+import org.jabref.model.database.BibDatabaseContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OpenConsoleAction extends SimpleCommand {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(OpenConsoleAction.class);
+    private final StateManager stateManager;
+
+    public OpenConsoleAction(StateManager stateManager) {
+        this.stateManager = stateManager;
+
+        this.executable.bind(ActionHelper.needsDatabase(stateManager));
+    }
+
+    @Override
+    public void execute() {
+        stateManager.getActiveDatabase().flatMap(BibDatabaseContext::getDatabasePath).ifPresent(path -> {
+                try {
+                    JabRefDesktop.openConsole(path.toFile());
+                } catch (IOException e) {
+                    LOGGER.info("Could not open console", e);
+                }
+        });
+    }
+}
diff --git a/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java b/src/main/java/org/jabref/gui/SendAsEMailAction.java
similarity index 65%
rename from src/main/java/org/jabref/gui/worker/SendAsEMailAction.java
rename to src/main/java/org/jabref/gui/SendAsEMailAction.java
index 24877673394..08f412b6a28 100644
--- a/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java
+++ b/src/main/java/org/jabref/gui/SendAsEMailAction.java
@@ -1,4 +1,4 @@
-package org.jabref.gui.worker;
+package org.jabref.gui;
 
 import java.awt.Desktop;
 import java.io.IOException;
@@ -9,15 +9,15 @@
 import java.util.List;
 
 import org.jabref.Globals;
-import org.jabref.gui.BasePanel;
-import org.jabref.gui.JabRefFrame;
-import org.jabref.gui.actions.BaseAction;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.gui.desktop.JabRefDesktop;
 import org.jabref.gui.util.BackgroundTask;
 import org.jabref.logic.bibtex.BibEntryWriter;
 import org.jabref.logic.bibtex.FieldWriter;
 import org.jabref.logic.l10n.Localization;
 import org.jabref.logic.util.io.FileUtil;
+import org.jabref.model.database.BibDatabaseContext;
 import org.jabref.model.entry.BibEntry;
 import org.jabref.preferences.JabRefPreferences;
 
@@ -34,49 +34,50 @@
  * are opened. This feature is disabled by default and can be switched on at
  * preferences/external programs
  */
-public class SendAsEMailAction implements BaseAction {
+public class SendAsEMailAction extends SimpleCommand {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(SendAsEMailAction.class);
-    private final JabRefFrame frame;
+    private DialogService dialogService;
+    private StateManager stateManager;
 
-    public SendAsEMailAction(JabRefFrame frame) {
-        this.frame = frame;
+    public SendAsEMailAction(DialogService dialogService, StateManager stateManager) {
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
+
+        this.executable.bind(ActionHelper.needsEntriesSelected(stateManager));
     }
 
     @Override
-    public void action() {
+    public void execute() {
         BackgroundTask.wrap(this::sendEmail)
-                      .onSuccess(frame.getDialogService()::notify)
+                      .onSuccess(dialogService::notify)
                       .onFailure(e -> {
                           String message = Localization.lang("Error creating email");
                           LOGGER.warn(message, e);
-                          frame.getDialogService().notify(message);
+                          dialogService.notify(message);
                       })
                       .executeWith(Globals.TASK_EXECUTOR);
     }
 
     private String sendEmail() throws Exception {
-        if (!Desktop.isDesktopSupported()) {
+        if (!Desktop.isDesktopSupported() || stateManager.getActiveDatabase().isEmpty()) {
             return Localization.lang("Error creating email");
         }
 
-        BasePanel panel = frame.getCurrentBasePanel();
-        if (panel == null) {
-            throw new IllegalStateException("Base panel is not available.");
-        }
-        if (panel.getSelectedEntries().isEmpty()) {
+        if (stateManager.getSelectedEntries().isEmpty()) {
             return Localization.lang("This operation requires one or more entries to be selected.");
         }
 
-        StringWriter sw = new StringWriter();
-        List<BibEntry> bes = panel.getSelectedEntries();
+        StringWriter rawEntries = new StringWriter();
+        BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get();
+        List<BibEntry> entries = stateManager.getSelectedEntries();
 
         // write the entries using sw, which is used later to form the email content
         BibEntryWriter bibtexEntryWriter = new BibEntryWriter(new FieldWriter(Globals.prefs.getFieldWriterPreferences()), Globals.entryTypesManager);
 
-        for (BibEntry entry : bes) {
+        for (BibEntry entry : entries) {
             try {
-                bibtexEntryWriter.write(entry, sw, panel.getBibDatabaseContext().getMode());
+                bibtexEntryWriter.write(entry, rawEntries, databaseContext.getMode());
             } catch (IOException e) {
                 LOGGER.warn("Problem creating BibTeX file for mailing.", e);
             }
@@ -88,20 +89,19 @@ private String sendEmail() throws Exception {
         //   the unofficial "mailto:attachment" property
         boolean openFolders = JabRefPreferences.getInstance().getBoolean(JabRefPreferences.OPEN_FOLDERS_OF_ATTACHED_FILES);
 
-        List<Path> fileList = FileUtil.getListOfLinkedFiles(bes, frame.getCurrentBasePanel().getBibDatabaseContext()
-                                                                      .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()));
-        for (Path f : fileList) {
-            attachments.add(f.toAbsolutePath().toString());
+        List<Path> fileList = FileUtil.getListOfLinkedFiles(entries, databaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()));
+        for (Path path : fileList) {
+            attachments.add(path.toAbsolutePath().toString());
             if (openFolders) {
                 try {
-                    JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath());
+                    JabRefDesktop.openFolderAndSelectFile(path.toAbsolutePath());
                 } catch (IOException e) {
                     LOGGER.debug("Cannot open file", e);
                 }
             }
         }
 
-        String mailTo = "?Body=".concat(sw.getBuffer().toString());
+        String mailTo = "?Body=".concat(rawEntries.getBuffer().toString());
         mailTo = mailTo.concat("&Subject=");
         mailTo = mailTo.concat(JabRefPreferences.getInstance().get(JabRefPreferences.EMAIL_SUBJECT));
         for (String path : attachments) {
@@ -114,6 +114,6 @@ private String sendEmail() throws Exception {
         Desktop desktop = Desktop.getDesktop();
         desktop.mail(uriMailTo);
 
-        return String.format("%s: %d", Localization.lang("Entries added to an email"), bes.size());
+        return String.format("%s: %d", Localization.lang("Entries added to an email"), entries.size());
     }
 }
diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java
index c09f97244f0..19a45f94c71 100644
--- a/src/main/java/org/jabref/gui/StateManager.java
+++ b/src/main/java/org/jabref/gui/StateManager.java
@@ -13,6 +13,7 @@
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.collections.ObservableMap;
+import javafx.scene.Node;
 
 import org.jabref.gui.util.OptionalObjectProperty;
 import org.jabref.logic.search.SearchQuery;
@@ -27,6 +28,7 @@
  * - currently selected group
  * - active search
  * - active number of search results
+ * - focus owner
  */
 public class StateManager {
 
@@ -36,6 +38,7 @@ public class StateManager {
     private final ObservableMap<BibDatabaseContext, ObservableList<GroupTreeNode>> selectedGroups = FXCollections.observableHashMap();
     private final OptionalObjectProperty<SearchQuery> activeSearchQuery = OptionalObjectProperty.empty();
     private final ObservableMap<BibDatabaseContext, IntegerProperty> searchResultMap = FXCollections.observableHashMap();
+    private final OptionalObjectProperty<Node> focusOwner = OptionalObjectProperty.empty();
 
     public StateManager() {
         activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null)));
@@ -99,4 +102,8 @@ public void clearSearchQuery() {
     public void setSearchQuery(SearchQuery searchQuery) {
         activeSearchQuery.setValue(Optional.of(searchQuery));
     }
+
+    public OptionalObjectProperty<Node> focusOwnerProperty() { return focusOwner; }
+
+    public Optional<Node> getFocusOwner() { return focusOwner.get(); }
 }
diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java
index dfd66772a3a..01e23d67e22 100644
--- a/src/main/java/org/jabref/gui/actions/ActionHelper.java
+++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java
@@ -1,9 +1,14 @@
 package org.jabref.gui.actions;
 
+import java.util.Collections;
+import java.util.List;
+
 import javafx.beans.binding.Bindings;
 import javafx.beans.binding.BooleanExpression;
 
 import org.jabref.gui.StateManager;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.Field;
 
 public class ActionHelper {
     public static BooleanExpression needsDatabase(StateManager stateManager) {
@@ -13,4 +18,22 @@ public static BooleanExpression needsDatabase(StateManager stateManager) {
     public static BooleanExpression needsEntriesSelected(StateManager stateManager) {
         return Bindings.isNotEmpty(stateManager.getSelectedEntries());
     }
+
+    public static BooleanExpression needsEntriesSelected(int numberOfEntries, StateManager stateManager) {
+        return Bindings.createBooleanBinding(
+                () -> stateManager.getSelectedEntries().size() == numberOfEntries,
+                stateManager.getSelectedEntries());
+    }
+
+    public static BooleanExpression isFieldSetForSelectedEntry(Field field, StateManager stateManager) {
+        return isAnyFieldSetForSelectedEntry(Collections.singletonList(field), stateManager);
+    }
+
+    public static BooleanExpression isAnyFieldSetForSelectedEntry(List<Field> fields, StateManager stateManager) {
+        BibEntry entry = stateManager.getSelectedEntries().get(0);
+        return Bindings.createBooleanBinding(
+                () -> entry.getFields().stream().anyMatch(fields::contains),
+                entry.getFieldsObservable(),
+                stateManager.getSelectedEntries());
+    }
 }
diff --git a/src/main/java/org/jabref/gui/actions/Actions.java b/src/main/java/org/jabref/gui/actions/Actions.java
index 49040b8e00b..f606eec20a1 100644
--- a/src/main/java/org/jabref/gui/actions/Actions.java
+++ b/src/main/java/org/jabref/gui/actions/Actions.java
@@ -10,19 +10,6 @@ public enum Actions {
     ABBREVIATE_SHORTEST_UNIQUE,
     ADD_FILE_LINK,
     CLEANUP,
-    COPY,
-    COPY_CITATION_ASCII_DOC,
-    COPY_CITATION_XSLFO,
-    COPY_CITATION_HTML,
-    COPY_CITATION_RTF,
-    COPY_CITATION_TEXT,
-    COPY_KEY,
-    COPY_CITE_KEY,
-    COPY_KEY_AND_TITLE,
-    COPY_KEY_AND_LINK,
-    COPY_TITLE,
-    CUT,
-    DELETE,
     DOWNLOAD_FULL_TEXT,
     EDIT,
     EDIT_PREAMBLE,
@@ -31,15 +18,6 @@ public enum Actions {
     MAKE_KEY,
     MANAGE_SELECTORS,
     MERGE_DATABASE,
-    MERGE_ENTRIES,
-    MERGE_WITH_FETCHED_ENTRY,
-    NEXT_PREVIEW_STYLE,
-    OPEN_CONSOLE,
-    OPEN_EXTERNAL_FILE,
-    OPEN_FOLDER,
-    OPEN_URL,
-    PASTE,
-    PREVIOUS_PREVIEW_STYLE,
     PULL_CHANGES_FROM_SHARED_DATABASE,
     REDO,
     REPLACE_ALL,
@@ -47,26 +25,7 @@ public enum Actions {
     SAVE_AS,
     SAVE_SELECTED_AS_PLAIN,
     SELECT_ALL,
-    SEND_AS_EMAIL,
     TOGGLE_GROUPS,
     UNABBREVIATE,
-    UNDO,
-    WRITE_XMP,
-    PRINT_PREVIEW,
-    TOGGLE_PRINTED,
-    CLEAR_PRIORITY,
-    SET_PRIORITY_1,
-    SET_PRIORITY_2,
-    SET_PRIORITY_3,
-    TOGGLE_QUALITY_ASSURED,
-    CLEAR_RANK,
-    SET_RANK_1,
-    SET_RANK_2,
-    SET_RANK_3,
-    SET_RANK_4,
-    SET_RANK_5,
-    CLEAR_READ_STATUS,
-    SET_READ_STATUS_TO_READ,
-    SET_READ_STATUS_TO_SKIMMED,
-    TOGGLE_RELEVANCE
+    UNDO
 }
diff --git a/src/main/java/org/jabref/gui/actions/JabRefAction.java b/src/main/java/org/jabref/gui/actions/JabRefAction.java
index f04deb1157c..dfdc35330b2 100644
--- a/src/main/java/org/jabref/gui/actions/JabRefAction.java
+++ b/src/main/java/org/jabref/gui/actions/JabRefAction.java
@@ -58,7 +58,10 @@ private String getActionName(Action action, Command command) {
             return action.getText();
         } else {
             String commandName = command.getClass().getSimpleName();
-            if ((command instanceof OldDatabaseCommandWrapper) || (command instanceof OldCommandWrapper) || commandName.contains("EditAction")) {
+            if ((command instanceof OldDatabaseCommandWrapper)
+                    || commandName.contains("EditAction")
+                    || commandName.contains("CopyMoreAction")
+                    || commandName.contains("CopyCitationAction")) {
                 return command.toString();
             } else {
                 return commandName;
diff --git a/src/main/java/org/jabref/gui/actions/OldCommandWrapper.java b/src/main/java/org/jabref/gui/actions/OldCommandWrapper.java
deleted file mode 100644
index 028e0b153bb..00000000000
--- a/src/main/java/org/jabref/gui/actions/OldCommandWrapper.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.jabref.gui.actions;
-
-import javafx.beans.property.ReadOnlyDoubleProperty;
-
-import org.jabref.gui.BasePanel;
-import org.jabref.gui.util.BindingsHelper;
-
-import de.saxsys.mvvmfx.utils.commands.CommandBase;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This wraps the old Swing commands so that they fit into the new infrastructure.
- * In the long term, this class should be removed.
- */
-@Deprecated
-public class OldCommandWrapper extends CommandBase {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(OldCommandWrapper.class);
-
-    private final Actions command;
-    private final BasePanel panel;
-
-    public OldCommandWrapper(Actions command, BasePanel panel) {
-        this.command = command;
-        this.panel = panel;
-    }
-
-    @Override
-    public void execute() {
-        try {
-            panel.runCommand(command);
-        } catch (Throwable ex) {
-            LOGGER.debug("Cannot execute command " + command + ".", ex);
-        }
-    }
-
-    @Override
-    public double getProgress() {
-        return 0;
-    }
-
-    @Override
-    public ReadOnlyDoubleProperty progressProperty() {
-        return null;
-    }
-
-    public void setExecutable(boolean executable) {
-        this.executable.bind(BindingsHelper.constantOf(executable));
-    }
-
-    @Override
-    public String toString() {
-        return this.command.toString();
-    }
-}
diff --git a/src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java b/src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java
deleted file mode 100644
index 23c2f6b86c9..00000000000
--- a/src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.jabref.gui.actions;
-
-import javafx.beans.property.ReadOnlyDoubleProperty;
-
-import org.jabref.JabRefGUI;
-import org.jabref.gui.util.BindingsHelper;
-
-import de.saxsys.mvvmfx.utils.commands.CommandBase;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This wraps the old Swing commands so that they fit into the new infrastructure.
- * In the long term, this class should be removed.
- */
-@Deprecated
-public class OldCommandWrapperForActiveDatabase extends CommandBase {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(OldCommandWrapperForActiveDatabase.class);
-
-    private final Actions command;
-
-    public OldCommandWrapperForActiveDatabase(Actions command) {
-        this.command = command;
-    }
-
-    @Override
-    public void execute() {
-        try {
-            JabRefGUI.getMainFrame().getCurrentBasePanel().runCommand(command);
-        } catch (Throwable ex) {
-            LOGGER.debug("Cannot execute command " + command + ".", ex);
-        }
-    }
-
-    @Override
-    public double getProgress() {
-        return 0;
-    }
-
-    @Override
-    public ReadOnlyDoubleProperty progressProperty() {
-        return null;
-    }
-
-    public void setExecutable(boolean executable) {
-        this.executable.bind(BindingsHelper.constantOf(executable));
-    }
-}
diff --git a/src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java b/src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java
deleted file mode 100644
index 86e0912400a..00000000000
--- a/src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.jabref.gui.edit;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.jabref.JabRefGUI;
-import org.jabref.gui.ClipBoardManager;
-import org.jabref.gui.JabRefDialogService;
-import org.jabref.gui.actions.BaseAction;
-import org.jabref.gui.maintable.MainTable;
-import org.jabref.gui.util.DefaultTaskExecutor;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.util.OS;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.field.StandardField;
-
-/**
- * This class will copy each selected entry's BibTeX key as a hyperlink to its url to the clipboard.
- * In case an entry doesn't have a BibTeX key it will not be copied.
- * In case an entry doesn't have an url this will only copy the BibTeX key.
- */
-public class CopyBibTeXKeyAndLinkAction implements BaseAction {
-
-    private final MainTable mainTable;
-    private final ClipBoardManager clipboardManager;
-
-    public CopyBibTeXKeyAndLinkAction(MainTable mainTable, ClipBoardManager clipboardManager) {
-        this.mainTable = mainTable;
-        this.clipboardManager = clipboardManager;
-    }
-
-    @Override
-    public void action() throws Exception {
-        List<BibEntry> entries = mainTable.getSelectedEntries();
-        if (!entries.isEmpty()) {
-            StringBuilder sb = new StringBuilder();
-
-            List<BibEntry> entriesWithKey = entries.stream().filter(BibEntry::hasCiteKey).collect(Collectors.toList());
-
-            if (entriesWithKey.isEmpty()) {
-                JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("None of the selected entries have BibTeX keys."));
-                return;
-            }
-
-            for (BibEntry entry : entriesWithKey) {
-                String key = entry.getCiteKeyOptional().get();
-                String url = entry.getField(StandardField.URL).orElse("");
-                sb.append(url.isEmpty() ? key : String.format("<a href=\"%s\">%s</a>", url, key));
-                sb.append(OS.NEWLINE);
-            }
-            final String keyAndLink = sb.toString();
-            DefaultTaskExecutor.runInJavaFXThread(() -> clipboardManager.setHtmlContent(keyAndLink));
-
-            int copied = entriesWithKey.size();
-            int toCopy = entries.size();
-            if (copied == toCopy) {
-                // All entries had keys.
-                JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(keyAndLink) + "'.");
-            } else {
-                JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
-                        Long.toString(toCopy - copied), Integer.toString(toCopy)));
-            }
-        }
-    }
-}
diff --git a/src/main/java/org/jabref/gui/edit/CopyMoreAction.java b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java
new file mode 100644
index 00000000000..c2bb13170d0
--- /dev/null
+++ b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java
@@ -0,0 +1,238 @@
+package org.jabref.gui.edit;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.jabref.Globals;
+import org.jabref.gui.ClipBoardManager;
+import org.jabref.gui.DialogService;
+import org.jabref.gui.JabRefDialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.actions.StandardActions;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.layout.Layout;
+import org.jabref.logic.layout.LayoutHelper;
+import org.jabref.logic.util.OS;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.StandardField;
+import org.jabref.preferences.JabRefPreferences;
+import org.jabref.preferences.PreferencesService;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CopyMoreAction extends SimpleCommand {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(CopyMoreAction.class);
+    private final StandardActions action;
+    private final DialogService dialogService;
+    private final StateManager stateManager;
+    private final ClipBoardManager clipBoardManager;
+    private final PreferencesService preferencesService;
+
+    public CopyMoreAction(StandardActions action, DialogService dialogService, StateManager stateManager, ClipBoardManager clipBoardManager, PreferencesService preferencesService) {
+        this.action = action;
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
+        this.clipBoardManager = clipBoardManager;
+        this.preferencesService = preferencesService;
+
+        this.executable.bind(ActionHelper.needsEntriesSelected(stateManager));
+    }
+
+    @Override
+    public void execute() {
+        if (stateManager.getActiveDatabase().isEmpty() || stateManager.getSelectedEntries().isEmpty()) {
+            return;
+        }
+
+        switch (action) {
+            case COPY_TITLE:
+                copyTitle();
+                break;
+            case COPY_KEY:
+                copyKey();
+                break;
+            case COPY_CITE_KEY:
+                copyCiteKey();
+                break;
+            case COPY_KEY_AND_TITLE:
+                copyKeyAndTitle();
+                break;
+            case COPY_KEY_AND_LINK:
+                copyKeyAndLink();
+                break;
+            default:
+                LOGGER.info("Unknown copy command.");
+                break;
+        }
+    }
+
+    private void copyTitle() {
+        List<BibEntry> selectedBibEntries = stateManager.getSelectedEntries();
+
+        List<String> titles = selectedBibEntries.stream()
+                                                .filter(bibEntry -> bibEntry.getTitle().isPresent())
+                                                .map(bibEntry -> bibEntry.getTitle().get())
+                                                .collect(Collectors.toList());
+
+        if (titles.isEmpty()) {
+            dialogService.notify(Localization.lang("None of the selected entries have titles."));
+            return;
+        }
+
+        final String copiedTitles = String.join("\n", titles);
+        clipBoardManager.setContent(copiedTitles);
+
+        if (titles.size() == selectedBibEntries.size()) {
+            // All entries had titles.
+            dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
+                    JabRefDialogService.shortenDialogMessage(copiedTitles)));
+        } else {
+            dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined title.",
+                    Integer.toString(selectedBibEntries.size() - titles.size()), Integer.toString(selectedBibEntries.size())));
+        }
+    }
+
+    private void copyKey() {
+        List<BibEntry> entries = stateManager.getSelectedEntries();
+
+        // Collect all non-null keys.
+        List<String> keys = entries.stream()
+                                   .filter(entry -> entry.getCiteKeyOptional().isPresent())
+                                   .map(entry -> entry.getCiteKeyOptional().get())
+                                   .collect(Collectors.toList());
+
+        if (keys.isEmpty()) {
+            dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys."));
+            return;
+        }
+
+        final String copiedKeys = String.join(",", keys);
+        clipBoardManager.setContent(copiedKeys);
+
+        if (keys.size() == entries.size()) {
+            // All entries had keys.
+            dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
+                    JabRefDialogService.shortenDialogMessage(copiedKeys)));
+        } else {
+            dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
+                    Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size())));
+        }
+    }
+
+    private void copyCiteKey() {
+        List<BibEntry> entries = stateManager.getSelectedEntries();
+
+        // Collect all non-null keys.
+        List<String> keys = entries.stream()
+                                   .filter(entry -> entry.getCiteKeyOptional().isPresent())
+                                   .map(entry -> entry.getCiteKeyOptional().get())
+                                   .collect(Collectors.toList());
+
+        if (keys.isEmpty()) {
+            dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys."));
+            return;
+        }
+
+        String citeCommand = Optional.ofNullable(Globals.prefs.get(JabRefPreferences.CITE_COMMAND))
+                                     .filter(cite -> cite.contains("\\")) // must contain \
+                                     .orElse("\\cite");
+
+        final String copiedCiteCommand = citeCommand + "{" + String.join(",", keys) + '}';
+        clipBoardManager.setContent(copiedCiteCommand);
+
+        if (keys.size() == entries.size()) {
+            // All entries had keys.
+            dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
+                    JabRefDialogService.shortenDialogMessage(copiedCiteCommand)));
+        } else {
+            dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
+                    Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size())));
+        }
+    }
+
+    private void copyKeyAndTitle() {
+        List<BibEntry> entries = stateManager.getSelectedEntries();
+
+        // ToDo: this string should be configurable to allow arbitrary exports
+        StringReader layoutString = new StringReader("\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n");
+        Layout layout;
+        try {
+            layout = new LayoutHelper(layoutString, preferencesService.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader)).getLayoutFromText();
+        } catch (IOException e) {
+            LOGGER.info("Could not get layout.", e);
+            return;
+        }
+
+        StringBuilder keyAndTitle = new StringBuilder();
+
+        int entriesWithKeys = 0;
+        // Collect all non-null keys.
+        for (BibEntry entry : entries) {
+            if (entry.hasCiteKey()) {
+                entriesWithKeys++;
+                keyAndTitle.append(layout.doLayout(entry, stateManager.getActiveDatabase().get().getDatabase()));
+            }
+        }
+
+        if (entriesWithKeys == 0) {
+            dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys."));
+            return;
+        }
+
+        clipBoardManager.setContent(keyAndTitle.toString());
+
+        if (entriesWithKeys == entries.size()) {
+            // All entries had keys.
+            dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
+                    JabRefDialogService.shortenDialogMessage(keyAndTitle.toString())));
+        } else {
+            dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
+                    Integer.toString(entries.size() - entriesWithKeys), Integer.toString(entries.size())));
+        }
+    }
+
+    /**
+     * This method will copy each selected entry's BibTeX key as a hyperlink to its url to the clipboard. In case an
+     * entry doesn't have a BibTeX key it will not be copied. In case an entry doesn't have an url this will only copy
+     * the BibTeX key.
+     */
+    private void copyKeyAndLink() {
+        List<BibEntry> entries = stateManager.getSelectedEntries();
+
+        StringBuilder keyAndLink = new StringBuilder();
+
+        List<BibEntry> entriesWithKey = entries.stream()
+                                               .filter(BibEntry::hasCiteKey)
+                                               .collect(Collectors.toList());
+
+        if (entriesWithKey.isEmpty()) {
+            dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys."));
+            return;
+        }
+
+        for (BibEntry entry : entriesWithKey) {
+            String key = entry.getCiteKeyOptional().get();
+            String url = entry.getField(StandardField.URL).orElse("");
+            keyAndLink.append(url.isEmpty() ? key : String.format("<a href=\"%s\">%s</a>", url, key));
+            keyAndLink.append(OS.NEWLINE);
+        }
+
+        clipBoardManager.setHtmlContent(keyAndLink.toString());
+
+        if (entriesWithKey.size() == entries.size()) {
+            // All entries had keys.
+            dialogService.notify(Localization.lang("Copied '%0' to clipboard.",
+                    JabRefDialogService.shortenDialogMessage(keyAndLink.toString())));
+        } else {
+            dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.",
+                    Long.toString(entries.size() - entriesWithKey.size()), Integer.toString(entries.size())));
+        }
+    }
+}
diff --git a/src/main/java/org/jabref/gui/edit/EditAction.java b/src/main/java/org/jabref/gui/edit/EditAction.java
new file mode 100644
index 00000000000..e48add2b08c
--- /dev/null
+++ b/src/main/java/org/jabref/gui/edit/EditAction.java
@@ -0,0 +1,76 @@
+package org.jabref.gui.edit;
+
+import javafx.scene.control.TextInputControl;
+
+import org.jabref.gui.JabRefFrame;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.actions.StandardActions;
+
+/**
+ * Class for handling general actions; cut, copy and paste. The focused component is kept track of by
+ * Globals.focusListener, and we call the action stored under the relevant name in its action map.
+ */
+public class EditAction extends SimpleCommand {
+
+        private final JabRefFrame frame;
+        private final StandardActions action;
+        private final StateManager stateManager;
+
+        public EditAction(StandardActions action, JabRefFrame frame, StateManager stateManager) {
+            this.action = action;
+            this.frame = frame;
+            this.stateManager = stateManager;
+
+            this.executable.bind(ActionHelper.needsEntriesSelected(stateManager));
+        }
+
+        @Override
+        public String toString() {
+            return this.action.toString();
+        }
+
+        @Override
+        public void execute() {
+            stateManager.getFocusOwner().ifPresent(focusOwner -> {
+                if (focusOwner instanceof TextInputControl) {
+                    // Focus is on text field -> copy/paste/cut selected text
+                    TextInputControl textInput = (TextInputControl) focusOwner;
+                    switch (action) {
+                        case COPY:
+                            textInput.copy();
+                            break;
+                        case CUT:
+                            textInput.cut();
+                            break;
+                        case PASTE:
+                            // handled by FX in TextInputControl#paste
+                            break;
+                        default:
+                            throw new IllegalStateException("Only cut/copy/paste supported in TextInputControl but got " + action);
+                    }
+                } else {
+                    // Not sure what is selected -> copy/paste/cut selected entries
+
+                    // ToDo: Should be handled by BibDatabaseContext instead of BasePanel
+                    switch (action) {
+                        case COPY:
+                            frame.getCurrentBasePanel().copy();
+                            break;
+                        case CUT:
+                            frame.getCurrentBasePanel().cut();
+                            break;
+                        case PASTE:
+                            // handled by FX in TextInputControl#paste
+                            break;
+                        case DELETE_ENTRY:
+                            frame.getCurrentBasePanel().delete(false);
+                            break;
+                        default:
+                            throw new IllegalStateException("Only cut/copy/paste supported but got " + action);
+                    }
+                }
+        });
+    }
+}
diff --git a/src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java b/src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java
new file mode 100644
index 00000000000..03247c4fdd0
--- /dev/null
+++ b/src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java
@@ -0,0 +1,31 @@
+package org.jabref.gui.entryeditor;
+
+import org.jabref.gui.JabRefFrame;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.SimpleCommand;
+
+import static org.jabref.gui.actions.ActionHelper.needsDatabase;
+
+public class PreviewSwitchAction extends SimpleCommand {
+
+    public enum Direction { PREVIOUS, NEXT }
+
+    private final JabRefFrame frame;
+    private final Direction direction;
+
+    public PreviewSwitchAction(Direction direction, JabRefFrame frame, StateManager stateManager) {
+        this.frame = frame;
+        this.direction = direction;
+
+        this.executable.bind(needsDatabase(stateManager));
+    }
+
+    @Override
+    public void execute() {
+        if (direction == Direction.NEXT) {
+            frame.getCurrentBasePanel().getEntryEditor().nextPreviewStyle();
+        } else {
+            frame.getCurrentBasePanel().getEntryEditor().previousPreviewStyle();
+        }
+    }
+}
diff --git a/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java b/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java
index 6b4c736c9e0..46c7166c67c 100644
--- a/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java
+++ b/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java
@@ -21,9 +21,9 @@
 import javafx.stage.Stage;
 
 import org.jabref.Globals;
-import org.jabref.gui.BasePanel;
 import org.jabref.gui.DialogService;
 import org.jabref.gui.FXDialog;
+import org.jabref.gui.StateManager;
 import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.gui.util.BackgroundTask;
 import org.jabref.logic.l10n.Localization;
@@ -31,25 +31,28 @@
 import org.jabref.model.database.BibDatabase;
 import org.jabref.model.entry.BibEntry;
 
+import static org.jabref.gui.actions.ActionHelper.needsDatabase;
+
 public class WriteXMPAction extends SimpleCommand {
 
-    private final BasePanel basePanel;
-    private OptionsDialog optionsDialog;
+    private final StateManager stateManager;
+    private final DialogService dialogService;
 
-    private Collection<BibEntry> entries;
+    private OptionsDialog optionsDialog;
 
     private BibDatabase database;
+    private Collection<BibEntry> entries;
 
     private boolean shouldContinue = true;
-
     private int skipped;
     private int entriesChanged;
     private int errors;
-    private final DialogService dialogService;
 
-    public WriteXMPAction(BasePanel basePanel) {
-        this.basePanel = basePanel;
-        dialogService = basePanel.frame().getDialogService();
+    public WriteXMPAction(StateManager stateManager, DialogService dialogService) {
+        this.stateManager = stateManager;
+        this.dialogService = dialogService;
+
+        this.executable.bind(needsDatabase(stateManager));
     }
 
     @Override
@@ -60,9 +63,13 @@ public void execute() {
     }
 
     public void init() {
-        database = basePanel.getDatabase();
+        if (stateManager.getActiveDatabase().isEmpty()) {
+            return;
+        }
+
+        database = stateManager.getActiveDatabase().get().getDatabase();
         // Get entries and check if it makes sense to perform this operation
-        entries = basePanel.getSelectedEntries();
+        entries = stateManager.getSelectedEntries();
 
         if (entries.isEmpty()) {
 
@@ -97,7 +104,7 @@ public void init() {
     }
 
     private void writeXMP() {
-        if (!shouldContinue) {
+        if (!shouldContinue || stateManager.getActiveDatabase().isEmpty()) {
             return;
         }
 
@@ -105,7 +112,7 @@ private void writeXMP() {
             // Make a list of all PDFs linked from this entry:
             List<Path> files = entry.getFiles().stream()
                                     .filter(file -> file.getFileType().equalsIgnoreCase("pdf"))
-                                    .map(file -> file.findIn(basePanel.getBibDatabaseContext(), Globals.prefs.getFilePreferences()))
+                                    .map(file -> file.findIn(stateManager.getActiveDatabase().get(), Globals.prefs.getFilePreferences()))
                                     .filter(Optional::isPresent)
                                     .map(Optional::get)
                                     .collect(Collectors.toList());
diff --git a/src/main/java/org/jabref/gui/filelist/AttachFileAction.java b/src/main/java/org/jabref/gui/filelist/AttachFileAction.java
index 51fa192c115..e11bff8b4dc 100644
--- a/src/main/java/org/jabref/gui/filelist/AttachFileAction.java
+++ b/src/main/java/org/jabref/gui/filelist/AttachFileAction.java
@@ -1,12 +1,12 @@
 package org.jabref.gui.filelist;
 
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Optional;
 
-import org.jabref.Globals;
 import org.jabref.gui.BasePanel;
 import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
 import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.gui.externalfiletype.ExternalFileTypes;
 import org.jabref.gui.fieldeditors.LinkedFilesEditorViewModel;
@@ -14,32 +14,45 @@
 import org.jabref.gui.util.FileDialogConfiguration;
 import org.jabref.logic.l10n.Localization;
 import org.jabref.model.FieldChange;
+import org.jabref.model.database.BibDatabaseContext;
 import org.jabref.model.entry.BibEntry;
 import org.jabref.model.entry.LinkedFile;
-import org.jabref.preferences.JabRefPreferences;
+import org.jabref.preferences.PreferencesService;
 
 public class AttachFileAction extends SimpleCommand {
 
     private final BasePanel panel;
+    private final StateManager stateManager;
     private final DialogService dialogService;
+    private final PreferencesService preferencesService;
 
-    public AttachFileAction(BasePanel panel, DialogService dialogService) {
+    public AttachFileAction(BasePanel panel, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) {
         this.panel = panel;
+        this.stateManager = stateManager;
         this.dialogService = dialogService;
+        this.preferencesService = preferencesService;
+
+        this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager));
     }
 
     @Override
     public void execute() {
-        if (panel.getSelectedEntries().size() != 1) {
+        if (stateManager.getActiveDatabase().isEmpty()) {
+            dialogService.notify(Localization.lang("This operation requires an open library."));
+            return;
+        }
+
+        if (stateManager.getSelectedEntries().size() != 1) {
             dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
             return;
         }
 
-        BibEntry entry = panel.getSelectedEntries().get(0);
+        BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get();
+
+        BibEntry entry = stateManager.getSelectedEntries().get(0);
 
-        Path workingDirectory = panel.getBibDatabaseContext()
-                                     .getFirstExistingFileDir(Globals.prefs.getFilePreferences())
-                                     .orElse(Paths.get(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)));
+        Path workingDirectory = databaseContext.getFirstExistingFileDir(preferencesService.getFilePreferences())
+                                               .orElse(preferencesService.getWorkingDir());
 
         FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder()
                 .withInitialDirectory(workingDirectory)
@@ -47,7 +60,7 @@ public void execute() {
 
         dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(newFile -> {
             LinkedFile linkedFile = LinkedFilesEditorViewModel.fromFile(newFile,
-                    panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()),
+                    databaseContext.getFileDirectoriesAsPaths(preferencesService.getFilePreferences()),
                     ExternalFileTypes.getInstance());
 
             LinkedFileEditDialogView dialog = new LinkedFileEditDialogView(linkedFile);
diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java
index 935470b1599..2293be5e914 100644
--- a/src/main/java/org/jabref/gui/maintable/MainTable.java
+++ b/src/main/java/org/jabref/gui/maintable/MainTable.java
@@ -85,7 +85,7 @@ public MainTable(MainTableDataModel model, JabRefFrame frame,
                                                                       panel.showAndEdit(entry.getEntry());
                                                                   }
                                                               })
-                .withContextMenu(entry -> RightClickMenu.create(entry, keyBindingRepository, panel, frame.getDialogService()))
+                .withContextMenu(entry -> RightClickMenu.create(entry, keyBindingRepository, panel, frame.getDialogService(), Globals.stateManager, Globals.prefs))
                 .setOnDragDetected(this::handleOnDragDetected)
                 .setOnDragDropped(this::handleOnDragDropped)
                 .setOnDragOver(this::handleOnDragOver)
diff --git a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java
new file mode 100644
index 00000000000..6579eed3102
--- /dev/null
+++ b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java
@@ -0,0 +1,56 @@
+package org.jabref.gui.maintable;
+
+import java.util.List;
+
+import org.jabref.Globals;
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
+import org.jabref.gui.fieldeditors.LinkedFileViewModel;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.StandardField;
+import org.jabref.preferences.PreferencesService;
+
+public class OpenExternalFileAction extends SimpleCommand {
+
+    private final DialogService dialogService;
+    private final StateManager stateManager;
+    private final PreferencesService preferencesService;
+
+    public OpenExternalFileAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) {
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
+        this.preferencesService = preferencesService;
+
+        this.executable.bind(ActionHelper.isFieldSetForSelectedEntry(StandardField.FILE, stateManager)
+                .and(ActionHelper.needsEntriesSelected(1, stateManager)));
+    }
+
+    @Override
+    public void execute() {
+        stateManager.getActiveDatabase().ifPresent(databaseContext -> {
+            final List<BibEntry> selectedEntries = stateManager.getSelectedEntries();
+
+            if (selectedEntries.size() != 1) {
+                dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
+                return;
+            }
+
+            final BibEntry entry = selectedEntries.get(0);
+
+            LinkedFileViewModel linkedFileViewModel = new LinkedFileViewModel(
+                    entry.getFiles().get(0),
+                    entry,
+                    databaseContext,
+                    Globals.TASK_EXECUTOR,
+                    dialogService,
+                    preferencesService.getXMPPreferences(),
+                    preferencesService.getFilePreferences(),
+                    ExternalFileTypes.getInstance());
+            linkedFileViewModel.open();
+        });
+    }
+}
diff --git a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java
new file mode 100644
index 00000000000..a2c3c211785
--- /dev/null
+++ b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java
@@ -0,0 +1,43 @@
+package org.jabref.gui.maintable;
+
+import org.jabref.Globals;
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.externalfiletype.ExternalFileTypes;
+import org.jabref.gui.fieldeditors.LinkedFileViewModel;
+import org.jabref.model.entry.field.StandardField;
+import org.jabref.preferences.PreferencesService;
+
+public class OpenFolderAction extends SimpleCommand {
+
+    private final DialogService dialogService;
+    private final StateManager stateManager;
+    private final PreferencesService preferencesService;
+
+    public OpenFolderAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) {
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
+        this.preferencesService = preferencesService;
+
+        this.executable.bind(ActionHelper.isFieldSetForSelectedEntry(StandardField.FILE, stateManager));
+    }
+
+    @Override
+    public void execute() {
+        stateManager.getActiveDatabase().ifPresent(databaseContext ->
+            stateManager.getSelectedEntries().forEach(entry -> {
+                LinkedFileViewModel linkedFileViewModel = new LinkedFileViewModel(
+                        entry.getFiles().get(0),
+                        entry,
+                        databaseContext,
+                        Globals.TASK_EXECUTOR,
+                        dialogService,
+                        preferencesService.getXMPPreferences(),
+                        preferencesService.getFilePreferences(),
+                        ExternalFileTypes.getInstance());
+                linkedFileViewModel.openFolder();
+            }));
+    }
+}
diff --git a/src/main/java/org/jabref/gui/maintable/OpenUrlAction.java b/src/main/java/org/jabref/gui/maintable/OpenUrlAction.java
new file mode 100644
index 00000000000..cfcd40b262c
--- /dev/null
+++ b/src/main/java/org/jabref/gui/maintable/OpenUrlAction.java
@@ -0,0 +1,72 @@
+package org.jabref.gui.maintable;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import javafx.beans.binding.BooleanExpression;
+
+import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.desktop.JabRefDesktop;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.StandardField;
+
+public class OpenUrlAction extends SimpleCommand {
+
+    private final DialogService dialogService;
+    private final StateManager stateManager;
+
+    public OpenUrlAction(DialogService dialogService, StateManager stateManager) {
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
+
+        BooleanExpression fieldIsSet = ActionHelper.isAnyFieldSetForSelectedEntry(
+                List.of(StandardField.URL, StandardField.DOI, StandardField.URI, StandardField.EPRINT),
+                stateManager);
+        this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager).and(fieldIsSet));
+    }
+
+    @Override
+    public void execute() {
+        stateManager.getActiveDatabase().ifPresent(databaseContext -> {
+            final List<BibEntry> entries = stateManager.getSelectedEntries();
+
+            if (entries.size() != 1) {
+                dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
+                return;
+            }
+
+            BibEntry entry = entries.get(0);
+
+            // ToDo: Create dialog or menu to chose which one to open
+            // URL - DOI - DOI - EPRINT
+            Optional<String> link = entry.getField(StandardField.EPRINT);
+            Field field = StandardField.EPRINT;
+            if (entry.hasField(StandardField.URI)) {
+                link = entry.getField(StandardField.URI);
+                field = StandardField.URI;
+            }
+            if (entry.hasField(StandardField.DOI)) {
+                link = entry.getField(StandardField.DOI);
+                field = StandardField.DOI;
+            }
+            if (entry.hasField(StandardField.URL)) {
+                link = entry.getField(StandardField.URL);
+                field = StandardField.URL;
+            }
+
+            if (link.isPresent()) {
+                try {
+                    JabRefDesktop.openExternalViewer(databaseContext, link.get(), field);
+                } catch (IOException e) {
+                    dialogService.showErrorDialogAndWait(Localization.lang("Unable to open link."), e);
+                }
+            }
+        });
+    }
+}
diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
index 7029810a2fa..279211c7457 100644
--- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
+++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java
@@ -1,8 +1,5 @@
 package org.jabref.gui.maintable;
 
-import java.util.Collections;
-import java.util.List;
-
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.Menu;
 import javafx.scene.control.SeparatorMenuItem;
@@ -10,134 +7,98 @@
 import org.jabref.Globals;
 import org.jabref.gui.BasePanel;
 import org.jabref.gui.DialogService;
+import org.jabref.gui.SendAsEMailAction;
+import org.jabref.gui.StateManager;
 import org.jabref.gui.actions.ActionFactory;
-import org.jabref.gui.actions.Actions;
-import org.jabref.gui.actions.OldCommandWrapper;
 import org.jabref.gui.actions.StandardActions;
+import org.jabref.gui.edit.CopyMoreAction;
+import org.jabref.gui.edit.EditAction;
 import org.jabref.gui.exporter.ExportToClipboardAction;
 import org.jabref.gui.filelist.AttachFileAction;
 import org.jabref.gui.keyboard.KeyBindingRepository;
 import org.jabref.gui.menus.ChangeEntryTypeMenu;
-import org.jabref.gui.mergeentries.FetchAndMergeEntry;
+import org.jabref.gui.mergeentries.MergeEntriesAction;
+import org.jabref.gui.mergeentries.MergeWithFetchedEntryAction;
+import org.jabref.gui.preview.CopyCitationAction;
 import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory;
+import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
 import org.jabref.logic.citationstyle.CitationStylePreviewLayout;
 import org.jabref.logic.citationstyle.PreviewLayout;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.field.Field;
 import org.jabref.model.entry.field.SpecialField;
-import org.jabref.model.entry.field.StandardField;
 import org.jabref.preferences.JabRefPreferences;
+import org.jabref.preferences.PreferencesService;
 import org.jabref.preferences.PreviewPreferences;
 
 public class RightClickMenu {
 
-    public static ContextMenu create(BibEntryTableViewModel entry, KeyBindingRepository keyBindingRepository, BasePanel panel, DialogService dialogService) {
+    public static ContextMenu create(BibEntryTableViewModel entry, KeyBindingRepository keyBindingRepository, BasePanel panel, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) {
         ContextMenu contextMenu = new ContextMenu();
         ActionFactory factory = new ActionFactory(keyBindingRepository);
 
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.COPY, new OldCommandWrapper(Actions.COPY, panel)));
-        contextMenu.getItems().add(createCopySubMenu(panel, factory, dialogService));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.PASTE, new OldCommandWrapper(Actions.PASTE, panel)));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.CUT, new OldCommandWrapper(Actions.CUT, panel)));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.DELETE, new OldCommandWrapper(Actions.DELETE, panel)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.COPY, new EditAction(StandardActions.COPY, panel.frame(), stateManager)));
+        contextMenu.getItems().add(createCopySubMenu(panel, factory, dialogService, stateManager, preferencesService));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, panel.frame(), stateManager)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, panel.frame(), stateManager)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, panel.frame(), stateManager)));
 
         contextMenu.getItems().add(new SeparatorMenuItem());
 
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new OldCommandWrapper(Actions.SEND_AS_EMAIL, panel)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsEMailAction(dialogService, stateManager)));
 
         contextMenu.getItems().add(new SeparatorMenuItem());
 
         if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) {
-            contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, panel));
-            contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, panel));
-            contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, panel));
-            contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, panel));
-            contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, panel));
-            contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, panel));
+            // ToDo: SpecialField needs the active BasePanel to mark it as changed.
+            //  Refactor BasePanel, should mark the BibDatabaseContext or the UndoManager as dirty instead!
+            contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, panel.frame(), dialogService, stateManager));
+            contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, panel.frame(), dialogService, stateManager));
+            contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, panel.frame(), dialogService, stateManager));
+            contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, panel.frame(), dialogService, stateManager));
+            contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, panel.frame(), dialogService, stateManager));
+            contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, panel.frame(), dialogService, stateManager));
         }
 
         contextMenu.getItems().add(new SeparatorMenuItem());
 
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_FOLDER, getOpenFolderCommand(panel)));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, getOpenExternalFileCommand(panel)));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_URL, getOpenUrlCommand(panel)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, preferencesService)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_URL, new OpenUrlAction(dialogService, stateManager)));
 
         contextMenu.getItems().add(new SeparatorMenuItem());
 
         contextMenu.getItems().add(new ChangeEntryTypeMenu().getChangeEntryTypeMenu(entry.getEntry(), panel.getBibDatabaseContext(), panel.getUndoManager()));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, getFetchEntryData(panel)));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.ATTACH_FILE, new AttachFileAction(panel, dialogService)));
-        contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_ENTRIES, mergeEntries(panel)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(panel, dialogService, stateManager)));
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.ATTACH_FILE, new AttachFileAction(panel, dialogService, stateManager, preferencesService)));
+        // ToDo: Refactor BasePanel, see ahead.
+        contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(panel.frame(), dialogService, stateManager)));
 
         return contextMenu;
     }
 
-    private static OldCommandWrapper mergeEntries(BasePanel panel) {
-        OldCommandWrapper command = new OldCommandWrapper(Actions.MERGE_ENTRIES, panel);
-        command.setExecutable(panel.getMainTable().getSelectedEntries().size() == 2);
-        return command;
-    }
-
-    private static OldCommandWrapper getFetchEntryData(BasePanel panel) {
-        OldCommandWrapper command = new OldCommandWrapper(Actions.MERGE_WITH_FETCHED_ENTRY, panel);
-        command.setExecutable(isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS, panel));
-        return command;
-    }
-
-    private static OldCommandWrapper getOpenUrlCommand(BasePanel panel) {
-        OldCommandWrapper command = new OldCommandWrapper(Actions.OPEN_URL, panel);
-        command.setExecutable(isFieldSetForSelectedEntry(StandardField.URL, panel) || isFieldSetForSelectedEntry(StandardField.DOI, panel));
-        return command;
-    }
-
-    private static OldCommandWrapper getOpenExternalFileCommand(BasePanel panel) {
-        OldCommandWrapper command = new OldCommandWrapper(Actions.OPEN_EXTERNAL_FILE, panel);
-        command.setExecutable(isFieldSetForSelectedEntry(StandardField.FILE, panel));
-        return command;
-    }
-
-    private static OldCommandWrapper getOpenFolderCommand(BasePanel panel) {
-        OldCommandWrapper command = new OldCommandWrapper(Actions.OPEN_FOLDER, panel);
-        command.setExecutable(isFieldSetForSelectedEntry(StandardField.FILE, panel));
-        return command;
-    }
-
-    private static Menu createCopySubMenu(BasePanel panel, ActionFactory factory, DialogService dialogService) {
+    private static Menu createCopySubMenu(BasePanel panel, ActionFactory factory, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) {
         Menu copySpecialMenu = factory.createMenu(StandardActions.COPY_MORE);
-        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_TITLE, new OldCommandWrapper(Actions.COPY_TITLE, panel)));
-        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY, new OldCommandWrapper(Actions.COPY_KEY, panel)));
-        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITE_KEY, new OldCommandWrapper(Actions.COPY_CITE_KEY, panel)));
-        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new OldCommandWrapper(Actions.COPY_KEY_AND_TITLE, panel)));
-        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new OldCommandWrapper(Actions.COPY_KEY_AND_LINK, panel)));
+        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_TITLE, new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, Globals.clipboardManager, preferencesService)));
+        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY, new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, Globals.clipboardManager, preferencesService)));
+        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITE_KEY, new CopyMoreAction(StandardActions.COPY_CITE_KEY, dialogService, stateManager, Globals.clipboardManager, preferencesService)));
+        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new CopyMoreAction(StandardActions.COPY_KEY_AND_TITLE, dialogService, stateManager, Globals.clipboardManager, preferencesService)));
+        copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new CopyMoreAction(StandardActions.COPY_KEY_AND_LINK, dialogService, stateManager, Globals.clipboardManager, preferencesService)));
 
         // the submenu will behave dependent on what style is currently selected (citation/preview)
         PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences();
         PreviewLayout style = previewPreferences.getCurrentPreviewStyle();
         if (style instanceof CitationStylePreviewLayout) {
-            copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_HTML, new OldCommandWrapper(Actions.COPY_CITATION_HTML, panel)));
+            copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_HTML, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.clipboardManager, previewPreferences)));
             Menu copyCitationMenu = factory.createMenu(StandardActions.COPY_CITATION_MORE);
-            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_TEXT, new OldCommandWrapper(Actions.COPY_CITATION_TEXT, panel)));
-            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_RTF, new OldCommandWrapper(Actions.COPY_CITATION_RTF, panel)));
-            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_ASCII_DOC, new OldCommandWrapper(Actions.COPY_CITATION_ASCII_DOC, panel)));
-            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_XSLFO, new OldCommandWrapper(Actions.COPY_CITATION_XSLFO, panel)));
+            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_TEXT, new CopyCitationAction(CitationStyleOutputFormat.TEXT, dialogService, stateManager, Globals.clipboardManager, previewPreferences)));
+            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_RTF, new CopyCitationAction(CitationStyleOutputFormat.RTF, dialogService, stateManager, Globals.clipboardManager, previewPreferences)));
+            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_ASCII_DOC, new CopyCitationAction(CitationStyleOutputFormat.ASCII_DOC, dialogService, stateManager, Globals.clipboardManager, previewPreferences)));
+            copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_XSLFO, new CopyCitationAction(CitationStyleOutputFormat.XSL_FO, dialogService, stateManager, Globals.clipboardManager, previewPreferences)));
             copySpecialMenu.getItems().add(copyCitationMenu);
         } else {
-            copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new OldCommandWrapper(Actions.COPY_CITATION_HTML, panel)));
+            copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.clipboardManager, previewPreferences)));
         }
 
         copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.EXPORT_TO_CLIPBOARD, new ExportToClipboardAction(panel, dialogService)));
         return copySpecialMenu;
     }
-
-    private static boolean isFieldSetForSelectedEntry(Field field, BasePanel panel) {
-        return isAnyFieldSetForSelectedEntry(Collections.singletonList(field), panel);
-    }
-
-    private static boolean isAnyFieldSetForSelectedEntry(List<Field> fields, BasePanel panel) {
-        if (panel.getMainTable().getSelectedEntries().size() == 1) {
-            BibEntry entry = panel.getMainTable().getSelectedEntries().get(0);
-            return !Collections.disjoint(fields, entry.getFields());
-        }
-        return false;
-    }
 }
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
index baca427159e..7ccc29e2515 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java
@@ -4,43 +4,47 @@
 import java.util.List;
 import java.util.Optional;
 
-import org.jabref.gui.BasePanel;
+import org.jabref.Globals;
 import org.jabref.gui.DialogService;
 import org.jabref.gui.JabRefFrame;
 import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
 import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.gui.undo.NamedCompound;
 import org.jabref.gui.undo.UndoableInsertEntries;
 import org.jabref.gui.undo.UndoableRemoveEntries;
 import org.jabref.logic.l10n.Localization;
+import org.jabref.model.database.BibDatabaseContext;
 import org.jabref.model.entry.BibEntry;
 
-import static org.jabref.gui.actions.ActionHelper.needsDatabase;
-
 public class MergeEntriesAction extends SimpleCommand {
 
-    private final JabRefFrame jabRefFrame;
+    private final JabRefFrame frame;
     private final DialogService dialogService;
+    private final StateManager stateManager;
 
-    public MergeEntriesAction(JabRefFrame jabRefFrame, StateManager stateManager) {
-        this.jabRefFrame = jabRefFrame;
-        this.dialogService = jabRefFrame.getDialogService();
+    public MergeEntriesAction(JabRefFrame frame, DialogService dialogService, StateManager stateManager) {
+        this.frame = frame;
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
 
-        this.executable.bind(needsDatabase(stateManager));
+        this.executable.bind(ActionHelper.needsEntriesSelected(2, stateManager));
     }
 
     @Override
     public void execute() {
-        BasePanel basePanel = jabRefFrame.getCurrentBasePanel();
+        if (stateManager.getActiveDatabase().isEmpty()) {
+            return;
+        }
+        BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get();
 
         // Check if there are two entries selected
-        List<BibEntry> selectedEntries = basePanel.getSelectedEntries();
+        List<BibEntry> selectedEntries = stateManager.getSelectedEntries();
         if (selectedEntries.size() != 2) {
             // Inform the user to select entries first.
             dialogService.showInformationDialogAndWait(
                     Localization.lang("Merge entries"),
                     Localization.lang("You have to choose exactly two entries to merge."));
-
             return;
         }
 
@@ -52,17 +56,20 @@ public void execute() {
         dlg.setTitle(Localization.lang("Merge entries"));
         Optional<BibEntry> mergedEntry = dlg.showAndWait();
         if (mergedEntry.isPresent()) {
-            basePanel.insertEntry(mergedEntry.get());
+            // ToDo: BibDatabase::insertEntry does not contain logic to mark the BasePanel as changed and to mark
+            //  entries with a timestamp, only BasePanel::insertEntry does. Workaround for the moment is to get the
+            //  BasePanel from the constructor injected JabRefFrame. Should be refactored and extracted!
+            frame.getCurrentBasePanel().insertEntry(mergedEntry.get());
 
             // Create a new entry and add it to the undo stack
             // Remove the other two entries and add them to the undo stack (which is not working...)
             NamedCompound ce = new NamedCompound(Localization.lang("Merge entries"));
-            ce.addEdit(new UndoableInsertEntries(basePanel.getDatabase(), mergedEntry.get()));
+            ce.addEdit(new UndoableInsertEntries(databaseContext.getDatabase(), mergedEntry.get()));
             List<BibEntry> entriesToRemove = Arrays.asList(one, two);
-            ce.addEdit(new UndoableRemoveEntries(basePanel.getDatabase(), entriesToRemove));
-            basePanel.getDatabase().removeEntries(entriesToRemove);
+            ce.addEdit(new UndoableRemoveEntries(databaseContext.getDatabase(), entriesToRemove));
+            databaseContext.getDatabase().removeEntries(entriesToRemove);
             ce.end();
-            basePanel.getUndoManager().addEdit(ce);
+            Globals.undoManager.addEdit(ce); // ToDo: Rework UndoManager and extract Globals
 
             dialogService.notify(Localization.lang("Merged entries"));
         } else {
diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java
index 2b2edf17017..e67c42b0df2 100644
--- a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java
+++ b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java
@@ -3,32 +3,38 @@
 import org.jabref.Globals;
 import org.jabref.gui.BasePanel;
 import org.jabref.gui.DialogService;
-import org.jabref.gui.actions.BaseAction;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.logic.l10n.Localization;
 import org.jabref.model.entry.BibEntry;
 import org.jabref.model.entry.field.OrFields;
 import org.jabref.model.entry.field.StandardField;
 
-public class MergeWithFetchedEntryAction implements BaseAction {
+public class MergeWithFetchedEntryAction extends SimpleCommand {
 
     private final BasePanel basePanel;
     private final DialogService dialogService;
+    private final StateManager stateManager;
 
-    public MergeWithFetchedEntryAction(BasePanel basePanel, DialogService dialogService) {
+    public MergeWithFetchedEntryAction(BasePanel basePanel, DialogService dialogService, StateManager stateManager) {
         this.basePanel = basePanel;
         this.dialogService = dialogService;
+        this.stateManager = stateManager;
+
+        this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager)
+                                         .and(ActionHelper.isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS, stateManager)));
     }
 
     @Override
-    public void action() {
-        if (basePanel.getMainTable().getSelectedEntries().size() == 1) {
-            BibEntry originalEntry = basePanel.getMainTable().getSelectedEntries().get(0);
-            new FetchAndMergeEntry(basePanel, Globals.TASK_EXECUTOR).fetchAndMerge(originalEntry);
-        } else {
+    public void execute() {
+        if (stateManager.getSelectedEntries().size() != 1) {
             dialogService.showInformationDialogAndWait(
                     Localization.lang("Merge entry with %0 information", new OrFields(StandardField.DOI, StandardField.ISBN, StandardField.EPRINT).getDisplayName()),
                     Localization.lang("This operation requires exactly one item to be selected."));
-
         }
+
+        BibEntry originalEntry = stateManager.getSelectedEntries().get(0);
+        new FetchAndMergeEntry(basePanel, Globals.TASK_EXECUTOR).fetchAndMerge(originalEntry);
     }
 }
diff --git a/src/main/java/org/jabref/gui/preview/CitationStyleToClipboardWorker.java b/src/main/java/org/jabref/gui/preview/CopyCitationAction.java
similarity index 89%
rename from src/main/java/org/jabref/gui/preview/CitationStyleToClipboardWorker.java
rename to src/main/java/org/jabref/gui/preview/CopyCitationAction.java
index 23a4f056d36..46e671147c2 100644
--- a/src/main/java/org/jabref/gui/preview/CitationStyleToClipboardWorker.java
+++ b/src/main/java/org/jabref/gui/preview/CopyCitationAction.java
@@ -3,16 +3,18 @@
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import javafx.scene.input.ClipboardContent;
 
 import org.jabref.Globals;
-import org.jabref.gui.BasePanel;
 import org.jabref.gui.ClipBoardManager;
 import org.jabref.gui.DialogService;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.gui.util.BackgroundTask;
-import org.jabref.gui.util.TaskExecutor;
 import org.jabref.logic.citationstyle.CitationStyleGenerator;
 import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
 import org.jabref.logic.citationstyle.CitationStylePreviewLayout;
@@ -32,33 +34,35 @@
  * Copies the selected entries and formats them with the selected citation style (or preview), then it is copied to the clipboard.
  * This worker cannot be reused.
  */
-public class CitationStyleToClipboardWorker {
+public class CopyCitationAction extends SimpleCommand {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(CitationStyleToClipboardWorker.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(CopyCitationAction.class);
 
-    private final BasePanel basePanel;
     private final List<BibEntry> selectedEntries;
+    private final StateManager stateManager;
     private final PreviewLayout style;
     private final String previewStyle;
     private final CitationStyleOutputFormat outputFormat;
     private final DialogService dialogService;
     private final ClipBoardManager clipBoardManager;
 
-    public CitationStyleToClipboardWorker(BasePanel basePanel, CitationStyleOutputFormat outputFormat, DialogService dialogService, ClipBoardManager clipBoardManager, PreviewPreferences previewPreferences) {
-        this.basePanel = basePanel;
-        this.selectedEntries = basePanel.getSelectedEntries();
+    public CopyCitationAction(CitationStyleOutputFormat outputFormat, DialogService dialogService, StateManager stateManager, ClipBoardManager clipBoardManager, PreviewPreferences previewPreferences) {
+        this.selectedEntries = stateManager.getSelectedEntries();
         this.style = previewPreferences.getCurrentPreviewStyle();
         this.previewStyle = previewPreferences.getPreviewStyle();
         this.outputFormat = outputFormat;
         this.clipBoardManager = clipBoardManager;
         this.dialogService = dialogService;
+        this.stateManager = stateManager;
+
+        this.executable.bind(ActionHelper.needsEntriesSelected(stateManager));
     }
 
-    public void copyCitationStyleToClipboard(TaskExecutor taskExecutor) {
+    public void execute() {
         BackgroundTask.wrap(this::generateCitations)
                       .onFailure(ex -> LOGGER.error("Error while copying citations to the clipboard", ex))
                       .onSuccess(this::setClipBoardContent)
-                      .executeWith(taskExecutor);
+                      .executeWith(Globals.TASK_EXECUTOR);
     }
 
     private List<String> generateCitations() throws IOException {
@@ -71,13 +75,17 @@ private List<String> generateCitations() throws IOException {
         if (styleSource != null) {
             return CitationStyleGenerator.generateCitations(selectedEntries, styleSource, outputFormat);
         } else {
+            if (stateManager.getActiveDatabase().isEmpty()) {
+                return Collections.emptyList();
+            }
+
             StringReader sr = new StringReader(previewStyle.replace("__NEWLINE__", "\n"));
             LayoutFormatterPreferences layoutFormatterPreferences = Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader);
             Layout layout = new LayoutHelper(sr, layoutFormatterPreferences).getLayoutFromText();
 
             List<String> citations = new ArrayList<>(selectedEntries.size());
             for (BibEntry entry : selectedEntries) {
-                citations.add(layout.doLayout(entry, basePanel.getDatabase()));
+                citations.add(layout.doLayout(entry, stateManager.getActiveDatabase().get().getDatabase()));
             }
             return citations;
         }
diff --git a/src/main/java/org/jabref/gui/specialfields/SpecialFieldAction.java b/src/main/java/org/jabref/gui/specialfields/SpecialFieldAction.java
index 395e14a28c7..1df63214b07 100644
--- a/src/main/java/org/jabref/gui/specialfields/SpecialFieldAction.java
+++ b/src/main/java/org/jabref/gui/specialfields/SpecialFieldAction.java
@@ -4,8 +4,11 @@
 import java.util.Objects;
 
 import org.jabref.Globals;
+import org.jabref.gui.DialogService;
 import org.jabref.gui.JabRefFrame;
-import org.jabref.gui.actions.BaseAction;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.ActionHelper;
+import org.jabref.gui.actions.SimpleCommand;
 import org.jabref.gui.undo.NamedCompound;
 import org.jabref.gui.undo.UndoableFieldChange;
 import org.jabref.logic.l10n.Localization;
@@ -17,16 +20,16 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SpecialFieldAction implements BaseAction {
+public class SpecialFieldAction extends SimpleCommand {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(SpecialFieldAction.class);
     private final JabRefFrame frame;
     private final SpecialField specialField;
     private final String value;
     private final boolean nullFieldIfValueIsTheSame;
-
     private final String undoText;
-
+    private final DialogService dialogService;
+    private final StateManager stateManager;
 
     /**
      * @param nullFieldIfValueIsTheSame - false also causes that doneTextPattern has two place holders %0 for the value and %1 for the sum of entries
@@ -36,18 +39,24 @@ public SpecialFieldAction(
             SpecialField specialField,
             String value,
             boolean nullFieldIfValueIsTheSame,
-            String undoText) {
+            String undoText,
+            DialogService dialogService,
+            StateManager stateManager) {
         this.frame = frame;
         this.specialField = specialField;
         this.value = value;
         this.nullFieldIfValueIsTheSame = nullFieldIfValueIsTheSame;
         this.undoText = undoText;
+        this.dialogService = dialogService;
+        this.stateManager = stateManager;
+
+        this.executable.bind(ActionHelper.needsEntriesSelected(stateManager));
     }
 
     @Override
-    public void action() {
+    public void execute() {
         try {
-            List<BibEntry> bes = frame.getCurrentBasePanel().getSelectedEntries();
+            List<BibEntry> bes = stateManager.getSelectedEntries();
             if ((bes == null) || bes.isEmpty()) {
                 return;
             }
@@ -70,7 +79,7 @@ public void action() {
                 } else {
                     outText = getTextDone(specialField, value, Integer.toString(bes.size()));
                 }
-                frame.getDialogService().notify(outText);
+                dialogService.notify(outText);
             } else {
                 // if user does not change anything with his action, we do not do anything either
                 // even no output message
@@ -83,7 +92,7 @@ public void action() {
     private String getTextDone(SpecialField field, String... params) {
         Objects.requireNonNull(params);
 
-        SpecialFieldViewModel viewModel = new SpecialFieldViewModel(field, frame.getCurrentBasePanel().getUndoManager());
+        SpecialFieldViewModel viewModel = new SpecialFieldViewModel(field, frame.getUndoManager());
 
         if (field.isSingleValueField() && (params.length == 1) && (params[0] != null)) {
             // Single value fields can be toggled only
diff --git a/src/main/java/org/jabref/gui/specialfields/SpecialFieldMenuItemFactory.java b/src/main/java/org/jabref/gui/specialfields/SpecialFieldMenuItemFactory.java
index 5cfa023b231..72c51d825bb 100644
--- a/src/main/java/org/jabref/gui/specialfields/SpecialFieldMenuItemFactory.java
+++ b/src/main/java/org/jabref/gui/specialfields/SpecialFieldMenuItemFactory.java
@@ -7,40 +7,34 @@
 import javafx.scene.control.Menu;
 import javafx.scene.control.MenuItem;
 
-import org.jabref.gui.BasePanel;
+import org.jabref.Globals;
+import org.jabref.gui.DialogService;
+import org.jabref.gui.JabRefFrame;
+import org.jabref.gui.StateManager;
 import org.jabref.gui.actions.ActionFactory;
-import org.jabref.gui.actions.OldCommandWrapper;
-import org.jabref.gui.actions.OldCommandWrapperForActiveDatabase;
 import org.jabref.model.entry.field.SpecialField;
 import org.jabref.model.entry.field.SpecialFieldValue;
 
 import de.saxsys.mvvmfx.utils.commands.Command;
 
 public class SpecialFieldMenuItemFactory {
-    public static MenuItem getSpecialFieldSingleItem(SpecialField field, ActionFactory factory, BasePanel panel) {
+    public static MenuItem getSpecialFieldSingleItem(SpecialField field, ActionFactory factory, JabRefFrame frame, DialogService dialogService, StateManager stateManager) {
         SpecialFieldValueViewModel specialField = new SpecialFieldValueViewModel(field.getValues().get(0));
-        return factory.createMenuItem(specialField.getAction(), new OldCommandWrapper(specialField.getCommand(), panel));
+        return factory.createMenuItem(specialField.getAction(),
+                new SpecialFieldViewModel(field, Globals.undoManager).getSpecialFieldAction(field.getValues().get(0), frame, dialogService, stateManager));
     }
 
-    public static MenuItem getSpecialFieldSingleItemForActiveDatabase(SpecialField field, ActionFactory factory) {
-        SpecialFieldValueViewModel specialField = new SpecialFieldValueViewModel(field.getValues().get(0));
-        return factory.createMenuItem(specialField.getAction(), new OldCommandWrapperForActiveDatabase(specialField.getCommand()));
-    }
-
-    public static Menu createSpecialFieldMenu(SpecialField field, ActionFactory factory, BasePanel panel) {
-        return createSpecialFieldMenu(field, factory, panel.getUndoManager(), specialField -> new OldCommandWrapper(specialField.getCommand(), panel));
-    }
-
-    public static Menu createSpecialFieldMenuForActiveDatabase(SpecialField field, ActionFactory factory, UndoManager undoManager) {
-        return createSpecialFieldMenu(field, factory, undoManager, specialField -> new OldCommandWrapperForActiveDatabase(specialField.getCommand()));
+    public static Menu createSpecialFieldMenu(SpecialField field, ActionFactory factory, JabRefFrame frame, DialogService dialogService, StateManager stateManager) {
+        return createSpecialFieldMenu(field, factory, Globals.undoManager, specialField ->
+                new SpecialFieldViewModel(field, Globals.undoManager).getSpecialFieldAction(specialField.getValue(), frame, dialogService, stateManager));
     }
 
     public static Menu createSpecialFieldMenu(SpecialField field, ActionFactory factory, UndoManager undoManager, Function<SpecialFieldValueViewModel, Command> commandFactory) {
         SpecialFieldViewModel viewModel = new SpecialFieldViewModel(field, undoManager);
         Menu menu = factory.createMenu(viewModel.getAction());
-        for (SpecialFieldValue val : field.getValues()) {
-            SpecialFieldValueViewModel specialField = new SpecialFieldValueViewModel(val);
-            menu.getItems().add(factory.createMenuItem(specialField.getAction(), commandFactory.apply(specialField)));
+        for (SpecialFieldValue Value : field.getValues()) {
+            SpecialFieldValueViewModel valueViewModel = new SpecialFieldValueViewModel(Value);
+            menu.getItems().add(factory.createMenuItem(valueViewModel.getAction(), commandFactory.apply(valueViewModel)));
         }
         return menu;
     }
diff --git a/src/main/java/org/jabref/gui/specialfields/SpecialFieldValueViewModel.java b/src/main/java/org/jabref/gui/specialfields/SpecialFieldValueViewModel.java
index 1a56592791f..2565adcfa42 100644
--- a/src/main/java/org/jabref/gui/specialfields/SpecialFieldValueViewModel.java
+++ b/src/main/java/org/jabref/gui/specialfields/SpecialFieldValueViewModel.java
@@ -4,7 +4,6 @@
 import java.util.Optional;
 
 import org.jabref.gui.actions.Action;
-import org.jabref.gui.actions.Actions;
 import org.jabref.gui.actions.StandardActions;
 import org.jabref.gui.icon.JabRefIcon;
 import org.jabref.logic.l10n.Localization;
@@ -71,45 +70,6 @@ public String getToolTipText() {
         }
     }
 
-    public Actions getCommand() {
-        switch (value) {
-            case PRINTED:
-                return Actions.TOGGLE_PRINTED;
-            case CLEAR_PRIORITY:
-                return Actions.CLEAR_PRIORITY;
-            case PRIORITY_HIGH:
-                return Actions.SET_PRIORITY_1;
-            case PRIORITY_MEDIUM:
-                return Actions.SET_PRIORITY_2;
-            case PRIORITY_LOW:
-                return Actions.SET_PRIORITY_3;
-            case QUALITY_ASSURED:
-                return Actions.TOGGLE_QUALITY_ASSURED;
-            case CLEAR_RANK:
-                return Actions.CLEAR_RANK;
-            case RANK_1:
-                return Actions.SET_RANK_1;
-            case RANK_2:
-                return Actions.SET_RANK_2;
-            case RANK_3:
-                return Actions.SET_RANK_3;
-            case RANK_4:
-                return Actions.SET_RANK_4;
-            case RANK_5:
-                return Actions.SET_RANK_5;
-            case CLEAR_READ_STATUS:
-                return Actions.CLEAR_READ_STATUS;
-            case READ:
-                return Actions.SET_READ_STATUS_TO_READ;
-            case SKIMMED:
-                return Actions.SET_READ_STATUS_TO_SKIMMED;
-            case RELEVANT:
-                return Actions.TOGGLE_RELEVANCE;
-            default:
-                throw new IllegalArgumentException("There is no action name for special field value " + value);
-        }
-    }
-
     public Action getAction() {
         switch (value) {
             case PRINTED:
@@ -145,7 +105,7 @@ public Action getAction() {
             case RELEVANT:
                 return StandardActions.RELEVANT;
             default:
-                throw new IllegalArgumentException("There is no tooltip localization for special field value " + value);
+                throw new IllegalArgumentException("There is no action name for special field value " + value);
         }
     }
 }
diff --git a/src/main/java/org/jabref/gui/specialfields/SpecialFieldViewModel.java b/src/main/java/org/jabref/gui/specialfields/SpecialFieldViewModel.java
index 455e1708c71..55c50a8324c 100644
--- a/src/main/java/org/jabref/gui/specialfields/SpecialFieldViewModel.java
+++ b/src/main/java/org/jabref/gui/specialfields/SpecialFieldViewModel.java
@@ -7,7 +7,9 @@
 import javax.swing.undo.UndoManager;
 
 import org.jabref.Globals;
+import org.jabref.gui.DialogService;
 import org.jabref.gui.JabRefFrame;
+import org.jabref.gui.StateManager;
 import org.jabref.gui.actions.Action;
 import org.jabref.gui.actions.StandardActions;
 import org.jabref.gui.icon.JabRefIcon;
@@ -32,12 +34,14 @@ public SpecialField getField() {
         return field;
     }
 
-    public SpecialFieldAction getSpecialFieldAction(SpecialFieldValue value, JabRefFrame frame) {
+    public SpecialFieldAction getSpecialFieldAction(SpecialFieldValue value, JabRefFrame frame, DialogService dialogService, StateManager stateManager) {
         return new SpecialFieldAction(frame, field, value.getFieldValue().orElse(null),
                 // if field contains only one value, it has to be nulled
                 // otherwise, another setting does not empty the field
                 field.getValues().size() == 1,
-                getLocalization());
+                getLocalization(),
+                dialogService,
+                stateManager);
     }
 
     public JabRefIcon getIcon() {
diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties
index 576ec67ee0d..5c3d694d816 100644
--- a/src/main/resources/l10n/JabRef_en.properties
+++ b/src/main/resources/l10n/JabRef_en.properties
@@ -170,7 +170,6 @@ Could\ not\ import\ preferences=Could not import preferences
 Could\ not\ instantiate\ %0=Could not instantiate %0
 Could\ not\ instantiate\ %0\ %1=Could not instantiate %0 %1
 Could\ not\ instantiate\ %0.\ Have\ you\ chosen\ the\ correct\ package\ path?=Could not instantiate %0. Have you chosen the correct package path?
-Could\ not\ open\ link=Could not open link
 
 Could\ not\ print\ preview=Could not print preview
 
@@ -331,8 +330,6 @@ External\ file\ links=External file links
 
 External\ programs=External programs
 
-External\ viewer\ called=External viewer called
-
 Field=Field
 
 field=field
@@ -566,7 +563,6 @@ No\ journal\ names\ could\ be\ unabbreviated.=No journal names could be unabbrev
 
 Open\ PDF=Open PDF
 
-No\ URL\ defined=No URL defined
 not=not
 
 not\ found=not found
@@ -2091,6 +2087,9 @@ Keyword\ delimiter=Keyword delimiter
 Hierarchical\ keyword\ delimiter=Hierarchical keyword delimiter
 Escape\ ampersands=Escape ampersands
 
+Copied\ '%0'\ to\ clipboard.=Copied '%0' to clipboard.
+This\ operation\ requires\ an\ open\ library.=This operation requires an open library.
+
 Plain\ References\ Parser=Plain References Parser
 Please\ enter\ the\ plain\ references\ to\ extract\ from\ separated\ by\ double\ empty\ lines.=Please enter the plain references to extract from separated by double empty lines.
 Add\ to\ current\ library=Add to current library
diff --git a/src/test/java/org/jabref/gui/preview/CitationStyleToClipboardWorkerTest.java b/src/test/java/org/jabref/gui/preview/CopyCitationActionTest.java
similarity index 94%
rename from src/test/java/org/jabref/gui/preview/CitationStyleToClipboardWorkerTest.java
rename to src/test/java/org/jabref/gui/preview/CopyCitationActionTest.java
index 82501d0e182..7dec89193c5 100644
--- a/src/test/java/org/jabref/gui/preview/CitationStyleToClipboardWorkerTest.java
+++ b/src/test/java/org/jabref/gui/preview/CopyCitationActionTest.java
@@ -11,7 +11,7 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-class CitationStyleToClipboardWorkerTest {
+class CopyCitationActionTest {
 
     @Test
     void processPreviewText() throws Exception {
@@ -39,7 +39,7 @@ void processPreviewText() throws Exception {
                 OS.NEWLINE +
                 "Abstract:  This entry describes a test scenario which may be useful in JabRef. By providing a test entry it is possible to see how certain things will look in this graphical BIB-file mananger. ";
 
-        ClipboardContent clipboardContent = CitationStyleToClipboardWorker.processPreview(Arrays.asList(citation, citation));
+        ClipboardContent clipboardContent = CopyCitationAction.processPreview(Arrays.asList(citation, citation));
         String actual = clipboardContent.getString();
 
         assertEquals(expected, actual);
@@ -92,7 +92,7 @@ void processPreviewHtml() throws Exception {
                 "</dd>" + OS.NEWLINE +
                 "<p></p></font>";
 
-        ClipboardContent clipboardContent = CitationStyleToClipboardWorker.processPreview(Arrays.asList(citation, citation));
+        ClipboardContent clipboardContent = CopyCitationAction.processPreview(Arrays.asList(citation, citation));
         String actual = clipboardContent.getString();
         assertEquals(expected, actual);
     }
@@ -103,7 +103,7 @@ void processText() throws Exception {
                 "[1]B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016." + OS.NEWLINE;
 
         String citation = "[1]B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016." + OS.NEWLINE;
-        ClipboardContent textTransferable = CitationStyleToClipboardWorker.processText(Arrays.asList(citation, citation));
+        ClipboardContent textTransferable = CopyCitationAction.processText(Arrays.asList(citation, citation));
 
         String actual = textTransferable.getString();
         assertEquals(expected, actual);
@@ -118,7 +118,7 @@ void processRtf() throws Exception {
                 "}";
 
         String citation = "[1]\\tab B. Smith, B. Jones, and J. Williams, \\uc0\\u8220{}Title of the test entry,\\uc0\\u8221{} {\\i{}BibTeX Journal}, vol. 34, no. 3, pp. 45\\uc0\\u8211{}67, Jul. 2016." + OS.NEWLINE;
-        ClipboardContent content = CitationStyleToClipboardWorker.processRtf(Arrays.asList(citation, citation));
+        ClipboardContent content = CopyCitationAction.processRtf(Arrays.asList(citation, citation));
 
         Object actual = content.getRtf();
         assertEquals(expected, actual);
@@ -191,7 +191,7 @@ void processXslFo() throws Exception {
                 "  </fo:table>" + OS.NEWLINE +
                 "</fo:block>" + OS.NEWLINE;
 
-        ClipboardContent xmlTransferable = CitationStyleToClipboardWorker.processXslFo(Arrays.asList(citation, citation));
+        ClipboardContent xmlTransferable = CopyCitationAction.processXslFo(Arrays.asList(citation, citation));
 
         Object actual = xmlTransferable.get(ClipBoardManager.XML);
         assertEquals(expected, actual);
@@ -221,7 +221,7 @@ void processHtmlAsHtml() throws Exception {
         String citation = "  <div class=\"csl-entry\">" + OS.NEWLINE +
                 "    <div class=\"csl-left-margin\">[1]</div><div class=\"csl-right-inline\">B. Smith, B. Jones, and J. Williams, “Title of the test entry,” <i>BibTeX Journal</i>, vol. 34, no. 3, pp. 45–67, Jul. 2016.</div>" + OS.NEWLINE +
                 "  </div>" + OS.NEWLINE;
-        ClipboardContent htmlTransferable = CitationStyleToClipboardWorker.processHtml(Arrays.asList(citation, citation));
+        ClipboardContent htmlTransferable = CopyCitationAction.processHtml(Arrays.asList(citation, citation));
 
         Object actual = htmlTransferable.getHtml();
         assertEquals(expected, actual);