Skip to content

Update entry for every change in the source panel #3366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.EntryType;
import org.jabref.model.entry.event.EntryChangedEvent;
import org.jabref.model.entry.event.FieldAddedOrRemovedEvent;
import org.jabref.preferences.JabRefPreferences;

Expand Down Expand Up @@ -196,7 +195,6 @@ public EntryEditor(BasePanel panel) {

public void setEntry(BibEntry entry) {
this.entry = Objects.requireNonNull(entry);
entry.registerListener(this);
entryType = EntryTypes.getTypeOrDefault(entry.getType(),
this.frame.getCurrentBasePanel().getBibDatabaseContext().getMode());

Expand All @@ -223,11 +221,6 @@ public synchronized void listen(FieldAddedOrRemovedEvent event) {
recalculateVisibleTabs();
}

@Subscribe
public synchronized void listen(EntryChangedEvent event) {
DefaultTaskExecutor.runInJavaFXThread(() -> sourceTab.updateSourcePane(entry));
}

/**
* Set-up key bindings specific for the entry editor.
*/
Expand Down Expand Up @@ -520,7 +513,6 @@ public void setMovingToDifferentEntry() {
}

private void unregisterListeners() {
this.entry.unregisterListener(this);
removeSearchListeners();

}
Expand Down
119 changes: 45 additions & 74 deletions src/main/java/org/jabref/gui/entryeditor/SourceTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@

import javax.swing.undo.UndoManager;

import javafx.scene.Node;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ListChangeListener;
import javafx.scene.control.Tooltip;

import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.DialogService;
import org.jabref.gui.FXDialogService;
import org.jabref.gui.IconTheme;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.UndoableChangeType;
import org.jabref.gui.undo.UndoableFieldChange;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.logic.bibtex.BibEntryWriter;
import org.jabref.logic.bibtex.InvalidFieldValueException;
import org.jabref.logic.bibtex.LatexFieldFormatter;
Expand All @@ -30,23 +33,24 @@
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.InternalBibtexFields;

import de.saxsys.mvvmfx.utils.validation.ObservableRuleBasedValidator;
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fxmisc.easybind.EasyBind;
import org.controlsfx.control.NotificationPane;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.CodeArea;

public class SourceTab extends EntryEditorTab {

private static final Log LOGGER = LogFactory.getLog(SourceTab.class);
private final BibDatabaseMode mode;
private final BasePanel panel;
private CodeArea codeArea;
private UndoManager undoManager;
private final ObjectProperty<ValidationMessage> sourceIsValid = new SimpleObjectProperty<>();
private final ObservableRuleBasedValidator sourceValidator = new ObservableRuleBasedValidator(sourceIsValid);

public SourceTab(BasePanel panel) {
this.mode = panel.getBibDatabaseContext().getMode();
this.panel = panel;
this.setText(Localization.lang("%0 source", mode.getFormattedName()));
this.setTooltip(new Tooltip(Localization.lang("Show/edit %0 source", mode.getFormattedName())));
this.setGraphic(IconTheme.JabRefIcon.SOURCE.getGraphicNode());
Expand All @@ -62,50 +66,12 @@ private static String getSourceString(BibEntry entry, BibDatabaseMode type) thro
return stringWriter.getBuffer().toString();
}

public void updateSourcePane(BibEntry entry) {
if (codeArea != null) {
try {
codeArea.clear();
codeArea.appendText(getSourceString(entry, mode));
} catch (IOException ex) {
codeArea.appendText(ex.getMessage() + "\n\n" +
Localization.lang("Correct the entry, and reopen editor to display/edit source."));
codeArea.setEditable(false);
LOGGER.debug("Incorrect entry", ex);
}
}
}

private Node createSourceEditor(BibDatabaseMode mode) {
codeArea = new CodeArea();
private CodeArea createSourceEditor() {
CodeArea codeArea = new CodeArea();
codeArea.setWrapText(true);
codeArea.lookup(".styled-text-area").setStyle(
"-fx-font-size: " + Globals.prefs.getFontSizeFX() + "pt;");
// store source if new tab is selected (if this one is not focused anymore)
EasyBind.subscribe(codeArea.focusedProperty(), focused -> {
if (!focused) {
storeSource();
}
});

try {
String srcString = getSourceString(this.currentEntry, mode);
codeArea.appendText(srcString);
} catch (IOException ex) {
codeArea.appendText(ex.getMessage() + "\n\n" +
Localization.lang("Correct the entry, and reopen editor to display/edit source."));
codeArea.setEditable(false);
LOGGER.debug("Incorrect entry", ex);
}

// set the database to dirty when something is changed in the source tab
EasyBind.subscribe(codeArea.beingUpdatedProperty(), updated -> {
if (updated) {
panel.markBaseChanged();
}
});

return new VirtualizedScrollPane<>(codeArea);
return codeArea;
}

@Override
Expand All @@ -115,17 +81,42 @@ public boolean shouldShow(BibEntry entry) {

@Override
protected void bindToEntry(BibEntry entry) {
this.setContent(createSourceEditor(mode));
CodeArea codeArea = createSourceEditor();
VirtualizedScrollPane<CodeArea> node = new VirtualizedScrollPane<>(codeArea);
NotificationPane notificationPane = new NotificationPane(node);
notificationPane.setShowFromTop(false);
sourceValidator.getValidationStatus().getMessages().addListener((ListChangeListener<ValidationMessage>) c -> {
while (c.next()) {
Platform.runLater(() -> sourceValidator.getValidationStatus().getHighestMessage().ifPresent(validationMessage -> notificationPane.show(validationMessage.getMessage())));
}
});
this.setContent(notificationPane);

// Store source for every change in the source code
// and update source code for every change of entry field values
BindingsHelper.bindContentBidirectional(entry.getFieldsObservable(), codeArea.textProperty(), this::storeSource, fields -> {
DefaultTaskExecutor.runInJavaFXThread(() -> {
codeArea.clear();
try {
codeArea.appendText(getSourceString(entry, mode));
} catch (IOException ex) {
codeArea.setEditable(false);
codeArea.appendText(ex.getMessage() + "\n\n" +
Localization.lang("Correct the entry, and reopen editor to display/edit source."));
LOGGER.debug("Incorrect entry", ex);
}
});
});
}

private void storeSource() {
if (codeArea.getText().isEmpty()) {
private void storeSource(String text) {
if (text.isEmpty()) {
return;
}

BibtexParser bibtexParser = new BibtexParser(Globals.prefs.getImportFormatPreferences());
try {
ParserResult parserResult = bibtexParser.parse(new StringReader(codeArea.getText()));
ParserResult parserResult = bibtexParser.parse(new StringReader(text));
BibDatabase database = parserResult.getDatabase();

if (database.getEntryCount() > 1) {
Expand Down Expand Up @@ -185,29 +176,9 @@ private void storeSource() {
}
compound.end();
undoManager.addEdit(compound);

} catch (InvalidFieldValueException | IOException ex) {
// The source couldn't be parsed, so the user is given an
// error message, and the choice to keep or revert the contents
// of the source text field.

} catch (InvalidFieldValueException | IllegalStateException | IOException ex) {
sourceIsValid.setValue(ValidationMessage.error(Localization.lang("Problem with parsing entry") + ": " + ex.getMessage()));
LOGGER.debug("Incorrect source", ex);
DialogService dialogService = new FXDialogService();
boolean keepEditing = dialogService.showConfirmationDialogAndWait(
Localization.lang("Problem with parsing entry"),
Localization.lang("Error") + ": " + ex.getMessage(),
Localization.lang("Edit"),
Localization.lang("Revert to original source")
);

if (!keepEditing) {
// Revert
try {
codeArea.replaceText(0, codeArea.getText().length(), getSourceString(this.currentEntry, mode));
} catch (IOException e) {
LOGGER.debug("Incorrect source", e);
}
}
}
}
}
62 changes: 62 additions & 0 deletions src/main/java/org/jabref/gui/util/BindingsHelper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jabref.gui.util;

import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
Expand All @@ -14,7 +15,9 @@
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.css.PseudoClass;
import javafx.scene.Node;

Expand Down Expand Up @@ -128,6 +131,25 @@ public static <A, B> void bindContentBidirectional(ListProperty<A> listProperty,
updateB);
}

public static <A, V, B> void bindContentBidirectional(ObservableMap<A, V> propertyA, ObservableValue<B> propertyB, Consumer<B> updateA, Consumer<Map<A, V>> updateB) {
final BidirectionalMapBinding<A, V, B> binding = new BidirectionalMapBinding<>(propertyA, propertyB, updateA, updateB);

// use list as initial source
updateB.accept(propertyA);

propertyA.addListener(binding);
propertyB.addListener(binding);
}

public static <A, V, B> void bindContentBidirectional(ObservableMap<A, V> propertyA, Property<B> propertyB, Consumer<B> updateA, Function<Map<A, V>, B> mapToB) {
Consumer<Map<A, V>> updateB = newValueList -> propertyB.setValue(mapToB.apply(newValueList));
bindContentBidirectional(
propertyA,
propertyB,
updateA,
updateB);
}

private static class BidirectionalBinding<A, B> {

private final ObservableValue<A> propertyA;
Expand Down Expand Up @@ -208,4 +230,44 @@ public void onChanged(Change<? extends A> c) {
}
}
}

private static class BidirectionalMapBinding<A, V, B> implements MapChangeListener<A, V>, ChangeListener<B> {

private final ObservableMap<A, V> mapProperty;
private final ObservableValue<B> property;
private final Consumer<B> updateA;
private final Consumer<Map<A, V>> updateB;
private boolean updating = false;

public BidirectionalMapBinding(ObservableMap<A, V> mapProperty, ObservableValue<B> property, Consumer<B> updateA, Consumer<Map<A, V>> updateB) {
this.mapProperty = mapProperty;
this.property = property;
this.updateA = updateA;
this.updateB = updateB;
}

@Override
public void changed(ObservableValue<? extends B> observable, B oldValue, B newValue) {
if (!updating) {
try {
updating = true;
updateA.accept(newValue);
} finally {
updating = false;
}
}
}

@Override
public void onChanged(Change<? extends A, ? extends V> c) {
if (!updating) {
try {
updating = true;
updateB.accept(mapProperty);
} finally {
updating = false;
}
}
}
}
}
4 changes: 4 additions & 0 deletions src/main/java/org/jabref/model/entry/BibEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,10 @@ public Optional<FieldChange> addFile(LinkedFile file) {
return setFiles(linkedFiles);
}

public ObservableMap<String, String> getFieldsObservable() {
return fields;
}

private interface GetFieldInterface {

Optional<String> getValueForField(String fieldName);
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_da.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=Slå_kun_strenge_op_for_standard

resolved=løst

Revert_to_original_source=Ret_tilbage_til_oprindelig_kildekode

Review=Kommentarer

Review_changes=Gennemse_ændringer
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=Strings_nur_für_Standard-BibTeX

resolved=davon_aufgelöst

Revert_to_original_source=Original_wiederherstellen

Review=Überprüfung

Review_changes=Änderungen_überprüfen
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_el.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=

resolved=

Revert_to_original_source=

Review=

Review_changes=
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=Resolve_strings_for_standard_Bib

resolved=resolved

Revert_to_original_source=Revert_to_original_source

Review=Review

Review_changes=Review_changes
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=Resolver_cadenas_únicamente_par

resolved=resuelto

Revert_to_original_source=Volver_a_la_fuente_original

Review=Revisar

Review_changes=Revisar_cambios
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_fa.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=

resolved=

Revert_to_original_source=

Review=

Review_changes=
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=Traiter_les_chaînes_pour_les_ch

resolved=résolu

Revert_to_original_source=Rétablir_le_contenu_initial

Review=Remarques

Review_changes=Revoir_les_changements
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/l10n/JabRef_in.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1025,8 +1025,6 @@ Resolve_strings_for_standard_BibTeX_fields_only=Selesaikan_masalah_string_hanya_

resolved=sudah_diselesaikan

Revert_to_original_source=Kembalikan_ke_sumber_asli

Review=Periksa_ulang

Review_changes=Periksa_ulang_perubahan
Expand Down
Loading