Skip to content
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

Improve Automatic Field Editor Dialog #8973

Merged
merged 81 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from 78 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
376760c
Update README.md
HoussemNasri Mar 24, 2022
d898da1
Merge branch 'JabRef:main' into main
HoussemNasri Mar 31, 2022
752954b
Merge branch 'JabRef:main' into main
HoussemNasri Apr 11, 2022
41b499f
Merge branch 'JabRef:main' into main
HoussemNasri Apr 26, 2022
a63045f
Merge branch 'JabRef:main' into main
HoussemNasri May 7, 2022
0901768
Merge branch 'JabRef:main' into main
HoussemNasri May 8, 2022
5874b4e
Merge remote-tracking branch 'origin/main' into main
HoussemNasri May 30, 2022
3c3fb02
Fix Readme
HoussemNasri Jun 7, 2022
81815fa
Merge branch 'JabRef:main' into main
HoussemNasri Jun 7, 2022
18d95c2
Merge branch 'JabRef:main' into main
HoussemNasri Jun 18, 2022
0e08af3
Merge branch 'JabRef:main' into main
HoussemNasri Jun 20, 2022
2bb0b6c
Merge branch 'JabRef:main' into main
HoussemNasri Jun 23, 2022
d71f93a
Commit
HoussemNasri Jun 23, 2022
57005ce
Merge branch 'JabRef:main' into main
HoussemNasri Jun 23, 2022
fffc86c
Update README.md
HoussemNasri Jun 29, 2022
4f480ba
Merge remote-tracking branch 'origin/main' into main
HoussemNasri Jul 12, 2022
63e09ba
Merge branch 'JabRef:main' into main
HoussemNasri Jul 12, 2022
365e968
Merge remote-tracking branch 'origin/main' into main
HoussemNasri Jul 12, 2022
033b9a2
Revert "Commit"
HoussemNasri Jul 12, 2022
1303d3c
Don't copy field value if it doesn't exist
HoussemNasri Jul 12, 2022
a4b5e3e
Refactor TwoFields tab to make it easier to test
HoussemNasri Jul 12, 2022
fd1006e
Test TwoFieldsViewModel
HoussemNasri Jul 12, 2022
2a1a298
Add more tests for TwoFieldsViewModel
HoussemNasri Jul 12, 2022
a8adb0b
Rename dialog's 'Revert' button to 'Cancel'
HoussemNasri Jul 12, 2022
0879542
Rename 'value' to 'content'
HoussemNasri Jul 12, 2022
675ed00
Make field ComboBox editable
HoussemNasri Jul 13, 2022
2bbb097
Update ComboBox field list when a new field is added
HoussemNasri Jul 13, 2022
ec75377
Rename "Overwrite Non empty fields" to "Overwrite field content"
HoussemNasri Jul 13, 2022
72e9287
Cancel changes when the dialog is closed using the X button
HoussemNasri Jul 13, 2022
120c63c
Revert "Cancel changes when the dialog is closed using the X button"
HoussemNasri Jul 13, 2022
0f9734a
Prevent JavaFX from firing CANCEL_CLOSE event
HoussemNasri Jul 13, 2022
7e58326
Show field display name in ComboBox
HoussemNasri Jul 13, 2022
d2f2b8a
Clear old field list before adding the new list
HoussemNasri Jul 15, 2022
bbe27b0
Rename 'TwoFields' to 'CopyOrMoveFieldContent'
HoussemNasri Jul 15, 2022
a70f750
i18n
HoussemNasri Jul 15, 2022
3a055ee
Rename package to copyormovecontent
HoussemNasri Jul 15, 2022
ccfd331
Make the set of visible fields observable
HoussemNasri Jul 15, 2022
b0accbf
Bind fields ComboBox to visible fields in BibDatabase
HoussemNasri Jul 15, 2022
b012f5d
Checkstyle
HoussemNasri Jul 15, 2022
4d6cd83
Make visibleFields set modifiable
HoussemNasri Jul 15, 2022
e2edbe1
Select first item in the ComboBox after fields set is updated
HoussemNasri Jul 15, 2022
46673e4
Bind RenameFieldTabView's combobox to visible fields set
HoussemNasri Jul 15, 2022
4663df5
Allow users to move or copy content from one field to a NEW field
HoussemNasri Jul 15, 2022
c1f2bf2
Bind CopyOrMoveFieldContentTabView's from/to fields combobox to visib…
HoussemNasri Jul 15, 2022
839454f
Remove the binding between visible fields in BibDatabase and ComboBox…
HoussemNasri Jul 17, 2022
35c3146
Add Standard fields even when visible fields set is empty
HoussemNasri Jul 17, 2022
bad23ae
Make all ComboBox(s) editable
HoussemNasri Jul 17, 2022
8652b58
Pass BibDatabase rather than BibDatabaseContext to field editor tabs
HoussemNasri Jul 17, 2022
b115260
Bind UI to view model and not the other way around
HoussemNasri Jul 17, 2022
e851dc0
Add getSelectedField() to EditFieldContentViewModel
HoussemNasri Jul 17, 2022
5bb34ff
Test EditFieldContentTabViewModel
HoussemNasri Jul 17, 2022
1e9dfc4
Remove System.out.println
HoussemNasri Jul 19, 2022
452d212
Show a notification to indicate the number of affected entries when a…
HoussemNasri Jul 21, 2022
c27b835
Don't register copy edit if the copy action is not performed
HoussemNasri Jul 21, 2022
ed18b44
Show affected rows notification when field values are swapped
HoussemNasri Jul 21, 2022
f9c45f9
Show affected rows notification when field values are set/cleared or …
HoussemNasri Jul 21, 2022
0e8c9a4
Add methods to register/unregister event bus to RenameFieldTabView
HoussemNasri Jul 21, 2022
2522645
Post a notification event when field is renamed or when field value i…
HoussemNasri Jul 21, 2022
0d44a15
Remove Notification Pane's transparent background style
HoussemNasri Jul 21, 2022
f06b212
Display the notification pane for 2 seconds
HoussemNasri Jul 21, 2022
b41033f
Convert AutomaticFieldEditorEvent to a record
HoussemNasri Jul 21, 2022
8011f30
Filter out blank field names
HoussemNasri Jul 21, 2022
e87dc4c
Reuse FIELD_STRING_CONVERTER from CustomEntryTypeDialogViewModel
HoussemNasri Jul 21, 2022
a5c84fa
Disable rename field button when either field ComboBox or new field n…
HoussemNasri Jul 21, 2022
3ceec86
Display an error if new field name has any whitespace characters
HoussemNasri Jul 21, 2022
2146766
Validate user input in EditFieldContentTabView
HoussemNasri Jul 21, 2022
aaa7814
Refactor
HoussemNasri Jul 21, 2022
dd7875d
Validate user input in CopyOrMoveFieldContentTabView
HoussemNasri Jul 21, 2022
cdc88ab
Test RenameFieldViewModel
HoussemNasri Jul 22, 2022
872839f
Convert to field display name as user types text in fromFieldComboBox
HoussemNasri Jul 22, 2022
9016c1a
Update 'containsWhitespace' implementation
HoussemNasri Jul 24, 2022
edc9f3e
Test containsWhitespace
HoussemNasri Jul 24, 2022
f80232a
Increase StringUtil maximum line count to 765
HoussemNasri Jul 24, 2022
96c7ea8
Stop using event bus in EditFieldContentTabView
HoussemNasri Jul 29, 2022
e925826
Stop using event bus in the other tabs
HoussemNasri Jul 29, 2022
d89f691
Delete AutomaticFieldEditorEvent.java
HoussemNasri Jul 29, 2022
1c9f063
Minimize number of constructor arguments
HoussemNasri Jul 29, 2022
e9a7716
Update tests
HoussemNasri Jul 29, 2022
6b86397
Work around exception
koppor Aug 1, 2022
f59c5f5
Fix casing
koppor Aug 1, 2022
f37c5f8
Convert to ParameterizedTests
koppor Aug 1, 2022
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
17 changes: 17 additions & 0 deletions src/main/java/org/jabref/gui/StateManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.ReadOnlyListWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.util.Pair;

import org.jabref.gui.edit.automaticfiededitor.LastAutomaticFieldEditorEdit;
import org.jabref.gui.sidepane.SidePaneType;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.CustomLocalDragboard;
Expand Down Expand Up @@ -62,6 +65,8 @@ public class StateManager {
private final ObservableMap<String, DialogWindowState> dialogWindowStates = FXCollections.observableHashMap();
private final ObservableList<SidePaneType> visibleSidePanes = FXCollections.observableArrayList();

private final ObjectProperty<LastAutomaticFieldEditorEdit> lastAutomaticFieldEditorEdit = new SimpleObjectProperty<>();

public StateManager() {
activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null)));
}
Expand Down Expand Up @@ -172,4 +177,16 @@ public DialogWindowState getDialogWindowState(String className) {
public void setDialogWindowState(String className, DialogWindowState state) {
dialogWindowStates.put(className, state);
}

public ObjectProperty<LastAutomaticFieldEditorEdit> lastAutomaticFieldEditorEditProperty() {
return lastAutomaticFieldEditorEdit;
}

public LastAutomaticFieldEditorEdit getLastAutomaticFieldEditorEdit() {
return lastAutomaticFieldEditorEditProperty().get();
}

public void setLastAutomaticFieldEditorEdit(LastAutomaticFieldEditorEdit automaticFieldEditorEdit) {
lastAutomaticFieldEditorEditProperty().set(automaticFieldEditorEdit);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.jabref.gui.edit.automaticfiededitor;

import java.util.Collection;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.StateManager;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractAutomaticFieldEditorTabViewModel extends AbstractViewModel {
public static final Logger LOGGER = LoggerFactory.getLogger(AbstractAutomaticFieldEditorTabViewModel.class);

protected final StateManager stateManager;

private final ObservableList<Field> allFields = FXCollections.observableArrayList();

public AbstractAutomaticFieldEditorTabViewModel(BibDatabase bibDatabase, StateManager stateManager) {
Objects.requireNonNull(bibDatabase);
Objects.requireNonNull(stateManager);
this.stateManager = stateManager;

addFields(EnumSet.allOf(StandardField.class));
addFields(bibDatabase.getAllVisibleFields());
allFields.sort(Comparator.comparing(Field::getName));
}

public ObservableList<Field> getAllFields() {
return allFields;
}

private void addFields(Collection<? extends Field> fields) {
Set<Field> fieldsSet = new HashSet<>(allFields);
fieldsSet.addAll(fields);
allFields.setAll(fieldsSet);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public AutomaticFieldEditorAction(StateManager stateManager, DialogService dialo

@Override
public void execute() {
dialogService.showCustomDialogAndWait(new AutomaticFieldEditorDialog(stateManager.getSelectedEntries(),
stateManager.getActiveDatabase().orElseThrow()));
dialogService.showCustomDialogAndWait(new AutomaticFieldEditorDialog(stateManager));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@
<TabPane fx:id="tabPane" tabClosingPolicy="UNAVAILABLE"/>
</content>
<ButtonType fx:id="saveButton" text="Keep Modifications" buttonData="OK_DONE"/>
<ButtonType fx:id="cancelButton"
text="Revert" buttonData="CANCEL_CLOSE"/>
<ButtonType fx:constant="CANCEL"/>
</DialogPane>
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
package org.jabref.gui.edit.automaticfiededitor;

import java.util.ArrayList;
import java.util.List;

import javax.swing.undo.UndoManager;

import javafx.fxml.FXML;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;

import org.jabref.gui.Globals;
import org.jabref.gui.StateManager;
import org.jabref.gui.util.BaseDialog;
import org.jabref.gui.util.ControlHelper;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;

import com.airhacks.afterburner.views.ViewLoader;
import com.tobiasdiez.easybind.EasyBind;

public class AutomaticFieldEditorDialog extends BaseDialog<Void> {
@FXML public ButtonType saveButton;
@FXML public ButtonType cancelButton;
public class AutomaticFieldEditorDialog extends BaseDialog<String> {
@FXML
private TabPane tabPane;

private final UndoManager undoManager;

private final BibDatabaseContext databaseContext;
private final BibDatabase database;
private final List<BibEntry> selectedEntries;

private final StateManager stateManager;

private AutomaticFieldEditorViewModel viewModel;

public AutomaticFieldEditorDialog(List<BibEntry> selectedEntries, BibDatabaseContext databaseContext) {
this.selectedEntries = selectedEntries;
this.databaseContext = databaseContext;
private List<NotificationPaneAdapter> notificationPanes = new ArrayList<>();

public AutomaticFieldEditorDialog(StateManager stateManager) {
this.selectedEntries = stateManager.getSelectedEntries();
this.database = stateManager.getActiveDatabase().orElseThrow().getDatabase();
this.stateManager = stateManager;
this.undoManager = Globals.undoManager;

this.setTitle(Localization.lang("Automatic field editor"));
Expand All @@ -42,8 +47,14 @@ public AutomaticFieldEditorDialog(List<BibEntry> selectedEntries, BibDatabaseCon
.load()
.setAsDialogPane(this);

ControlHelper.setAction(saveButton, getDialogPane(), event -> saveChangesAndCloseDialog());
ControlHelper.setAction(cancelButton, getDialogPane(), event -> cancelChangesAndCloseDialog());
setResultConverter(buttonType -> {
if (buttonType != null && buttonType.getButtonData() == ButtonBar.ButtonData.OK_DONE) {
saveChanges();
} else {
cancelChanges();
}
return "";
});

// This will prevent all dialog buttons from having the same size
// Read more: https://stackoverflow.com/questions/45866249/javafx-8-alert-different-button-sizes
Expand All @@ -54,20 +65,26 @@ public AutomaticFieldEditorDialog(List<BibEntry> selectedEntries, BibDatabaseCon

@FXML
public void initialize() {
viewModel = new AutomaticFieldEditorViewModel(selectedEntries, databaseContext, undoManager);
viewModel = new AutomaticFieldEditorViewModel(selectedEntries, database, undoManager, stateManager);

for (AutomaticFieldEditorTab tabModel : viewModel.getFieldEditorTabs()) {
tabPane.getTabs().add(new Tab(tabModel.getTabName(), tabModel.getContent()));
NotificationPaneAdapter notificationPane = new NotificationPaneAdapter(tabModel.getContent());
notificationPanes.add(notificationPane);
tabPane.getTabs().add(new Tab(tabModel.getTabName(), notificationPane));
}

EasyBind.listen(stateManager.lastAutomaticFieldEditorEditProperty(), (obs, old, lastEdit) -> {
viewModel.getDialogEdits().addEdit(lastEdit.getEdit());
notificationPanes.get(lastEdit.getTabIndex())
.notify(lastEdit.getAffectedEntries(), selectedEntries.size());
});
}

private void saveChangesAndCloseDialog() {
private void saveChanges() {
viewModel.saveChanges();
close();
}

private void cancelChangesAndCloseDialog() {
private void cancelChanges() {
viewModel.cancelChanges();
close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,32 @@
import javafx.collections.ObservableList;

import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.edit.automaticfiededitor.editfieldvalue.EditFieldValueTabView;
import org.jabref.gui.StateManager;
import org.jabref.gui.edit.automaticfiededitor.copyormovecontent.CopyOrMoveFieldContentTabView;
import org.jabref.gui.edit.automaticfiededitor.editfieldcontent.EditFieldContentTabView;
import org.jabref.gui.edit.automaticfiededitor.renamefield.RenameFieldTabView;
import org.jabref.gui.edit.automaticfiededitor.twofields.TwoFieldsTabView;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;

public class AutomaticFieldEditorViewModel extends AbstractViewModel {
public static final String NAMED_COMPOUND_EDITS = "EDIT_FIELDS";
private final ObservableList<AutomaticFieldEditorTab> fieldEditorTabs = FXCollections.observableArrayList();
private final NamedCompound dialogEdits = new NamedCompound(NAMED_COMPOUND_EDITS);

private final BibDatabaseContext databaseContext;
private final UndoManager undoManager;

public AutomaticFieldEditorViewModel(List<BibEntry> selectedEntries, BibDatabaseContext databaseContext, UndoManager undoManager) {
public AutomaticFieldEditorViewModel(List<BibEntry> selectedEntries, BibDatabase database, UndoManager undoManager, StateManager stateManager) {
this.undoManager = undoManager;
fieldEditorTabs.addAll(
new EditFieldValueTabView(selectedEntries, databaseContext, dialogEdits),
new TwoFieldsTabView(selectedEntries, databaseContext, dialogEdits),
new RenameFieldTabView(selectedEntries, databaseContext, dialogEdits)
new EditFieldContentTabView(database, stateManager),
new CopyOrMoveFieldContentTabView(database, stateManager),
new RenameFieldTabView(database, stateManager)
);
this.databaseContext = databaseContext;
this.undoManager = undoManager;
}

public NamedCompound getDialogEdits() {
return dialogEdits;
}

public ObservableList<AutomaticFieldEditorTab> getFieldEditorTabs() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.jabref.gui.edit.automaticfiededitor;

import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;

import org.jabref.gui.undo.NamedCompound;

public class LastAutomaticFieldEditorEdit extends AbstractUndoableEdit {
private final Integer affectedEntries;
private final NamedCompound edit;

private final Integer tabIndex;

public LastAutomaticFieldEditorEdit(Integer affectedEntries, Integer tabIndex, NamedCompound edit) {
this.affectedEntries = affectedEntries;
this.edit = edit;
this.tabIndex = tabIndex;
}

public Integer getAffectedEntries() {
return affectedEntries;
}

public NamedCompound getEdit() {
return edit;
}

public Integer getTabIndex() {
return tabIndex;
}

@Override
public void undo() throws CannotUndoException {
super.undo();
edit.undo();
}

@Override
public void redo() throws CannotRedoException {
super.redo();
edit.redo();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,48 @@ public class MoveFieldValueAction extends SimpleCommand {

private final NamedCompound edits;

public MoveFieldValueAction(Field fromField, Field toField, List<BibEntry> entries, NamedCompound edits) {
private int affectedEntriesCount;

private final boolean overwriteToFieldContent;

public MoveFieldValueAction(Field fromField, Field toField, List<BibEntry> entries, NamedCompound edits, boolean overwriteToFieldContent) {
this.fromField = fromField;
this.toField = toField;
this.entries = entries;
this.edits = edits;
this.overwriteToFieldContent = overwriteToFieldContent;
}

public MoveFieldValueAction(Field fromField, Field toField, List<BibEntry> entries, NamedCompound edits) {
this(fromField, toField, entries, edits, true);
}

@Override
public void execute() {
affectedEntriesCount = 0;
for (BibEntry entry : entries) {
String fromFieldValue = entry.getField(fromField).orElse("");
String toFieldValue = entry.getField(toField).orElse("");

if (StringUtil.isNotBlank(fromFieldValue)) {
entry.setField(toField, fromFieldValue);
entry.setField(fromField, "");

edits.addEdit(new UndoableFieldChange(entry, fromField, fromFieldValue, null));
edits.addEdit(new UndoableFieldChange(entry, toField, toFieldValue, fromFieldValue));
if (overwriteToFieldContent || toFieldValue.isEmpty()) {
entry.setField(toField, fromFieldValue);
entry.setField(fromField, "");

edits.addEdit(new UndoableFieldChange(entry, fromField, fromFieldValue, null));
edits.addEdit(new UndoableFieldChange(entry, toField, toFieldValue, fromFieldValue));
affectedEntriesCount++;
}
}
}

edits.end();
}

/**
* @return the number of affected entries
* */
public int executeAndGetAffectedEntriesCount() {
execute();
return affectedEntriesCount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jabref.gui.edit.automaticfiededitor;

import java.util.Collections;

import javafx.scene.Node;
import javafx.util.Duration;

import org.jabref.gui.LibraryTab;
import org.jabref.gui.icon.IconTheme;

public class NotificationPaneAdapter extends LibraryTab.DatabaseNotification {

public NotificationPaneAdapter(Node content) {
super(content);
}

public void notify(int affectedEntries, int totalEntries) {
String notificationMessage = String.format("%d/%d affected entries", affectedEntries, totalEntries);
Node notificationGraphic = IconTheme.JabRefIcons.INTEGRITY_INFO.getGraphicNode();

notify(notificationGraphic, notificationMessage, Collections.emptyList(), Duration.seconds(2));
}
}
Loading