diff --git a/CHANGELOG.md b/CHANGELOG.md index f665c96f50e..ca737e5b13d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We improved the way metadata is updated in remote databases. [#3235](https://github.com/JabRef/jabref/issues/3235) - We improved font rendering of the Entry Editor for Linux based systems [#3295](https://github.com/JabRef/jabref/issues/3295) - We fixed an issue where JabRef would freeze when trying to replace the original entry after a merge with new information from identifiers like DOI/ISBN etc. [3294](https://github.com/JabRef/jabref/issues/3294) - + - We fixed an issue where JabRef would not show the translated content at some points, although there existed a translation ### Removed diff --git a/build.gradle b/build.gradle index 8e15465c47d..e9a12b9f7f3 100644 --- a/build.gradle +++ b/build.gradle @@ -130,7 +130,7 @@ dependencies { compile group: 'com.microsoft.azure', name: 'applicationinsights-logging-log4j2', version: '1.0.9' testCompile 'junit:junit:4.12' - testCompile 'org.mockito:mockito-core:2.10.0' + testCompile 'org.mockito:mockito-core:2.11.0' testCompile 'com.github.tomakehurst:wiremock:2.8.0' testCompile 'org.assertj:assertj-swing-junit:3.8.0' testCompile 'org.reflections:reflections:0.9.11' diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java index 8ddcb93dd52..b5079bbcb1c 100644 --- a/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -519,7 +519,7 @@ private void regenerateBibtexKeys(List loaded) { database, entry, Globals.prefs.getBibtexKeyPatternPreferences()); } } else { - LOGGER.info(Localization.lang("No meta data present in BIB_file. Cannot regenerate BibTeX keys")); + LOGGER.info(Localization.lang("No meta data present in BIB file. Cannot regenerate BibTeX keys")); } } } diff --git a/src/main/java/org/jabref/gui/EntryTypeDialog.java b/src/main/java/org/jabref/gui/EntryTypeDialog.java index 13b711f284d..7a082facf61 100644 --- a/src/main/java/org/jabref/gui/EntryTypeDialog.java +++ b/src/main/java/org/jabref/gui/EntryTypeDialog.java @@ -331,7 +331,7 @@ protected void done() { } else if (searchID.trim().isEmpty()) { JOptionPane.showMessageDialog(frame, Localization.lang("The given search ID was empty."), Localization.lang("Empty search ID"), JOptionPane.WARNING_MESSAGE); } else if (!fetcherException) { - JOptionPane.showMessageDialog(frame, Localization.lang("Fetcher_'%0'_did_not_find_an_entry_for_id_'%1'.", fetcher.getName(), searchID) + "\n" + fetcherExceptionMessage, Localization.lang("No files found."), JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(frame, Localization.lang("Fetcher '%0' did not find an entry for id '%1'.", fetcher.getName(), searchID) + "\n" + fetcherExceptionMessage, Localization.lang("No files found."), JOptionPane.WARNING_MESSAGE); } else { JOptionPane.showMessageDialog(frame, Localization.lang("Error while fetching from %0", fetcher.getName()) + "." + "\n" + fetcherExceptionMessage, diff --git a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java index db8e820adc8..0d93ede3330 100644 --- a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java @@ -290,7 +290,7 @@ public static void openBrowserShowPopup(String url) { LOGGER.error("Could not open browser", exception); String couldNotOpenBrowser = Localization.lang("Could not open browser."); String openManually = Localization.lang("Please open %0 manually.", url); - String copiedToClipboard = Localization.lang("The_link_has_been_copied_to_the_clipboard."); + String copiedToClipboard = Localization.lang("The link has been copied to the clipboard."); JabRefGUI.getMainFrame().output(couldNotOpenBrowser); JOptionPane.showMessageDialog(JabRefGUI.getMainFrame(), couldNotOpenBrowser + "\n" + openManually + "\n" + copiedToClipboard, couldNotOpenBrowser, JOptionPane.ERROR_MESSAGE); diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java index a863536dfe8..4be110c1bc6 100644 --- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java @@ -1,20 +1,27 @@ package org.jabref.gui.entryeditor; +import java.util.List; + import javafx.scene.control.Tooltip; -import org.jabref.gui.BasePanel; import org.jabref.gui.IconTheme; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.EntryType; public class DeprecatedFieldsTab extends FieldsEditorTab { - public DeprecatedFieldsTab(JabRefFrame frame, BasePanel basePanel, EntryType entryType, EntryEditor parent, BibEntry entry) { - super(frame, basePanel, entryType.getDeprecatedFields(), parent, false, false, entry); + public DeprecatedFieldsTab(BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders) { + super(false, databaseContext, suggestionProviders); setText(Localization.lang("Deprecated fields")); setTooltip(new Tooltip(Localization.lang("Show deprecated BibTeX fields"))); setGraphic(IconTheme.JabRefIcon.OPTIONAL.getGraphicNode()); } + + @Override + protected List determineFieldsToShow(BibEntry entry, EntryType entryType) { + return entryType.getDeprecatedFields(); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index bb0e34f19fa..27c823bde94 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -12,6 +12,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -106,11 +107,6 @@ public class EntryEditor extends JPanel implements EntryContainer { private static final Log LOGGER = LogFactory.getLog(EntryEditor.class); - /** - * The default index number of the other fields tab - */ - private static final int OTHER_FIELDS_DEFAULTPOSITION = 4; - /** * A reference to the entry this object works on. */ @@ -152,6 +148,7 @@ public class EntryEditor extends JPanel implements EntryContainer { private final RedoAction redoAction = new RedoAction(); private final List searchListeners = new ArrayList<>(); private final JFXPanel container; + private final List tabs; /** * Indicates that we are about to go to the next or previous entry @@ -159,7 +156,6 @@ public class EntryEditor extends JPanel implements EntryContainer { private final BooleanProperty movingToDifferentEntry = new SimpleBooleanProperty(); private EntryType entryType; private SourceTab sourceTab; - private final BorderLayout layout; private TypeLabel typeLabel; public EntryEditor(BasePanel panel) { @@ -168,28 +164,34 @@ public EntryEditor(BasePanel panel) { writeXmp = new WriteXMPEntryEditorAction(panel, this); - layout = new BorderLayout(); + BorderLayout layout = new BorderLayout(); setLayout(layout); container = OS.LINUX ? new CustomJFXPanel() : new JFXPanel(); // Create type-label typeLabel = new TypeLabel(""); setupToolBar(); - DefaultTaskExecutor.runInJavaFXThread(() -> - container.setScene(new Scene(tabbed)) - ); + DefaultTaskExecutor.runInJavaFXThread(() -> { + tabbed.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); + tabbed.setStyle( + "-fx-font-size: " + Globals.prefs.getFontSizeFX() + "pt;" + + "-fx-open-tab-animation: NONE; -fx-close-tab-animation: NONE;"); + container.setScene(new Scene(tabbed)); + }); add(container, BorderLayout.CENTER); DefaultTaskExecutor.runInJavaFXThread(() -> { EasyBind.subscribe(tabbed.getSelectionModel().selectedItemProperty(), tab -> { EntryEditorTab activeTab = (EntryEditorTab) tab; if (activeTab != null) { - activeTab.notifyAboutFocus(); + activeTab.notifyAboutFocus(entry); } }); }); setupKeyBindings(); + + tabs = createTabs(); } public void setEntry(BibEntry entry) { @@ -201,73 +203,29 @@ public void setEntry(BibEntry entry) { displayedBibEntryType = entry.getType(); DefaultTaskExecutor.runInJavaFXThread(() -> { - addTabs(this.getVisibleTabName()); - - tabbed.setStyle("-fx-font-size: " + Globals.prefs.getFontSizeFX() + "pt;"); + recalculateVisibleTabs(); + if (Globals.prefs.getBoolean(JabRefPreferences.DEFAULT_SHOW_SOURCE)) { + tabbed.getSelectionModel().select(sourceTab); + } + // Notify current tab about new entry + EntryEditorTab selectedTab = (EntryEditorTab) tabbed.getSelectionModel().getSelectedItem(); + selectedTab.notifyAboutFocus(entry); }); + TypedBibEntry typedEntry = new TypedBibEntry(entry, panel.getBibDatabaseContext().getMode()); typeLabel.setText(typedEntry.getTypeForDisplay()); } @Subscribe public synchronized void listen(FieldAddedOrRemovedEvent event) { - // other field deleted -> update other fields tab - if (OtherFieldsTab.isOtherField(entryType, event.getFieldName())) { - DefaultTaskExecutor.runInJavaFXThread(() -> rebuildOtherFieldsTab()); - } + // Rebuild entry editor based on new information (e.g. hide/add tabs) + recalculateVisibleTabs(); } @Subscribe public synchronized void listen(EntryChangedEvent event) { - DefaultTaskExecutor.runInJavaFXThread(() -> sourceTab.updateSourcePane()); - } - - private void rebuildOtherFieldsTab() { - int index = -1; - boolean isOtherFieldsTabSelected = false; - - // find tab index and selection status - for (Tab tab : tabbed.getTabs()) { - if (tab instanceof OtherFieldsTab) { - index = tabbed.getTabs().indexOf(tab); - isOtherFieldsTabSelected = tabbed.getSelectionModel().isSelected(index); - break; - } - } - - // rebuild tab at index and with prior selection status - if (index != -1) { - readdOtherFieldsTab(index, isOtherFieldsTabSelected); - } else { - // maybe the tab wasn't there but needs to be now - addNewOtherFieldsTabIfNeeded(); - } - } - - private void readdOtherFieldsTab(int index, boolean isOtherFieldsTabSelected) { - tabbed.getTabs().remove(index); - OtherFieldsTab tab = new OtherFieldsTab(frame, panel, entryType, this, entry); - // if there are no other fields left, no need to readd the tab - if (!(tab.getFields().size() == 0)) { - tabbed.getTabs().add(index, tab); - } - // select the new tab if it was selected before - if (isOtherFieldsTabSelected) { - tabbed.getSelectionModel().select(tab); - } - } - - private void addNewOtherFieldsTabIfNeeded() { - OtherFieldsTab tab = new OtherFieldsTab(frame, panel, entryType, this, entry); - if (tab.getFields().size() > 0) { - // add it at default index, but that is just a guess - tabbed.getTabs().add(OTHER_FIELDS_DEFAULTPOSITION, tab); - } - } - - private void selectLastUsedTab(String lastTabName) { - tabbed.getTabs().stream().filter(tab -> lastTabName.equals(tab.getText())).findFirst().ifPresent(tab -> tabbed.getSelectionModel().select(tab)); + DefaultTaskExecutor.runInJavaFXThread(() -> sourceTab.updateSourcePane(entry)); } /** @@ -335,53 +293,62 @@ public void close() { closeAction.actionPerformed(null); } - private void addTabs(String lastTabName) { + private void recalculateVisibleTabs() { + List visibleTabs = tabs.stream().filter(tab -> tab.shouldShow(entry)).collect(Collectors.toList()); - List tabs = new ArrayList<>(); + // Start of ugly hack: + // We need to find out, which tabs will be shown and which not and remove and re-add the appropriate tabs + // to the editor. We don't want to simply remove all and re-add the complete list of visible tabs, because + // the tabs give an ugly animation the looks like all tabs are shifting in from the right. + // This hack is required since tabbed.getTabs().setAll(visibleTabs) changes the order of the tabs in the editor + + // First, remove tabs that we do not want to show + List toBeRemoved = tabs.stream().filter(tab -> !tab.shouldShow(entry)).collect(Collectors.toList()); + tabbed.getTabs().removeAll(toBeRemoved); + + // Next add all the visible tabs (if not already present) at the right position + for (int i = 0; i < visibleTabs.size(); i++) { + Tab toBeAdded = visibleTabs.get(i); + Tab shown = null; + if (i < tabbed.getTabs().size()) { + shown = tabbed.getTabs().get(i); + } + + if (!toBeAdded.equals(shown)) { + tabbed.getTabs().add(i, toBeAdded); + } + } + } + + private List createTabs() { + List tabs = new LinkedList<>(); // Required fields - tabs.add(new RequiredFieldsTab(frame, panel, entryType, this, entry)); + tabs.add(new RequiredFieldsTab(panel.getDatabaseContext(), panel.getSuggestionProviders())); // Optional fields - tabs.add(new OptionalFieldsTab(frame, panel, entryType, this, entry)); - tabs.add(new OptionalFields2Tab(frame, panel, entryType, this, entry)); - tabs.add(new DeprecatedFieldsTab(frame, panel, entryType, this, entry)); + tabs.add(new OptionalFieldsTab(panel.getDatabaseContext(), panel.getSuggestionProviders())); + tabs.add(new OptionalFields2Tab(panel.getDatabaseContext(), panel.getSuggestionProviders())); + tabs.add(new DeprecatedFieldsTab(panel.getDatabaseContext(), panel.getSuggestionProviders())); // Other fields - tabs.add(new OtherFieldsTab(frame, panel, entryType, this, entry)); + tabs.add(new OtherFieldsTab(panel.getDatabaseContext(), panel.getSuggestionProviders())); // General fields from preferences EntryEditorTabList tabList = Globals.prefs.getEntryEditorTabList(); for (int i = 0; i < tabList.getTabCount(); i++) { - FieldsEditorTab newFieldsEditorTab = new FieldsEditorTab(frame, panel, tabList.getTabFields(i), this, false, - false, entry); - newFieldsEditorTab.setText(tabList.getTabName(i)); - tabs.add(newFieldsEditorTab); + tabs.add(new UserDefinedFieldsTab(tabList.getTabName(i), tabList.getTabFields(i), panel.getDatabaseContext(), panel.getSuggestionProviders())); } // Special tabs - tabs.add(new MathSciNetTab(entry)); - tabs.add(new FileAnnotationTab(panel.getAnnotationCache(), entry)); - tabs.add(new RelatedArticlesTab(entry)); + tabs.add(new MathSciNetTab()); + tabs.add(new FileAnnotationTab(panel.getAnnotationCache())); + tabs.add(new RelatedArticlesTab(Globals.prefs)); // Source tab - sourceTab = new SourceTab(panel, entry, movingToDifferentEntry); + sourceTab = new SourceTab(panel, movingToDifferentEntry); tabs.add(sourceTab); - - tabbed.getTabs().clear(); - for (EntryEditorTab tab : tabs) { - if (tab.shouldShow()) { - tabbed.getTabs().add(tab); - } - } - tabbed.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); - - if (Globals.prefs.getBoolean(JabRefPreferences.DEFAULT_SHOW_SOURCE)) { - tabbed.getSelectionModel().select(sourceTab); - } else { - selectLastUsedTab(lastTabName); - } - + return tabs; } public String getDisplayedBibEntryType() { @@ -539,11 +506,10 @@ public void setVisibleTab(String name) { public void setFocusToField(String fieldName) { for (Tab tab : tabbed.getTabs()) { - if ((tab instanceof FieldsEditorTab) && ((FieldsEditorTab) tab).getFields().contains(fieldName)) { + if ((tab instanceof FieldsEditorTab) && ((FieldsEditorTab) tab).determineFieldsToShow(entry, entryType).contains(fieldName)) { FieldsEditorTab fieldsEditorTab = (FieldsEditorTab) tab; tabbed.getSelectionModel().select(tab); - fieldsEditorTab.setActive(fieldName); - fieldsEditorTab.focus(); + fieldsEditorTab.requestFocus(fieldName); } } } @@ -808,9 +774,7 @@ public void actionPerformed(ActionEvent event) { // Should only be done if this editor is currently showing: // don't select the current entry again (eg use BasePanel#highlightEntry} in case another entry was selected) if (!movingAway && isShowing()) { - SwingUtilities.invokeLater(() -> { - panel.getMainTable().ensureVisible(entry); - }); + SwingUtilities.invokeLater(() -> panel.getMainTable().ensureVisible(entry)); } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java index 4c752af5170..652cfcf2e3c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java @@ -2,28 +2,38 @@ import javafx.scene.control.Tab; +import org.jabref.model.entry.BibEntry; + public abstract class EntryEditorTab extends Tab { + protected BibEntry currentEntry; + /** - * Used for lazy-loading of the tab content. + * Decide whether to show this tab for the given entry. */ - protected boolean isInitialized = false; + public abstract boolean shouldShow(BibEntry entry); - public abstract boolean shouldShow(); - - public void requestFocus() { + /** + * Updates the view with the contents of the given entry. + */ + protected abstract void bindToEntry(BibEntry entry); + /** + * The tab just got the focus. Override this method if you want to perform a special action on focus (like selecting + * the first field in the editor) + */ + protected void handleFocus() { + // Do nothing by default } /** - * This method is called when the user focuses this tab. + * Notifies the tab that it got focus and should display the given entry. */ - public void notifyAboutFocus() { - if (!isInitialized) { - initialize(); - isInitialized = true; + public void notifyAboutFocus(BibEntry entry) { + if (!entry.equals(currentEntry)) { + currentEntry = entry; + bindToEntry(entry); } + handleFocus(); } - - protected abstract void initialize(); } diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 1b9b5581a3c..bfedb7a6b5f 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -1,11 +1,9 @@ package org.jabref.gui.entryeditor; import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -21,15 +19,17 @@ import javafx.scene.layout.RowConstraints; import org.jabref.Globals; -import org.jabref.gui.BasePanel; import org.jabref.gui.FXDialogService; import org.jabref.gui.GUIGlobals; -import org.jabref.gui.JabRefFrame; +import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.fieldeditors.FieldEditorFX; import org.jabref.gui.fieldeditors.FieldEditors; import org.jabref.gui.fieldeditors.FieldNameLabel; import org.jabref.logic.l10n.Localization; +import org.jabref.model.EntryTypes; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.EntryType; import org.jabref.model.entry.FieldName; import org.jabref.model.entry.FieldProperty; import org.jabref.model.entry.InternalBibtexFields; @@ -37,34 +37,19 @@ /** * A single tab displayed in the EntryEditor holding several FieldEditors. */ -class FieldsEditorTab extends EntryEditorTab { +abstract class FieldsEditorTab extends EntryEditorTab { - private final List fields; - private final EntryEditor parent; private final Map editors = new LinkedHashMap<>(); - private final JabRefFrame frame; - private final BasePanel basePanel; - private final BibEntry entry; private final boolean isCompressed; - private Region panel; - private FieldEditorFX activeField; + private final SuggestionProviders suggestionProviders; + private FieldEditorFX activeField; + private final BibDatabaseContext databaseContext; - public FieldsEditorTab(JabRefFrame frame, BasePanel basePanel, List fields, EntryEditor parent, boolean addKeyField, boolean compressed, BibEntry entry) { - this.entry = Objects.requireNonNull(entry); - this.fields = new ArrayList<>(Objects.requireNonNull(fields)); + public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders) { this.isCompressed = compressed; - // Add the edit field for Bibtex-key. - if (addKeyField) { - this.fields.add(BibEntry.KEY_FIELD); - } - - this.parent = parent; - this.frame = frame; - this.basePanel = basePanel; - - // The following line makes sure focus cycles inside tab instead of being lost to other parts of the frame: - //panel.setFocusCycleRoot(true); + this.databaseContext = databaseContext; + this.suggestionProviders = suggestionProviders; } private static void addColumn(GridPane gridPane, int columnIndex, List