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

[wip] Extract Entry Editor #12210

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion src/main/java/org/jabref/gui/JabRefGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ private void openWindow() {
}

public void onShowing(WindowEvent event) {
Platform.runLater(() -> mainFrame.updateDividerPosition());
Platform.runLater(() -> {
mainFrame.updateHorizontalDividerPosition();
mainFrame.updateVerticalDividerPosition();
});

// Open last edited databases
if (uiCommands.stream().noneMatch(UiCommand.BlankWorkspace.class::isInstance)
Expand Down
121 changes: 5 additions & 116 deletions src/main/java/org/jabref/gui/LibraryTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.swing.undo.UndoManager;
Expand All @@ -23,13 +22,11 @@
import javafx.beans.value.ObservableBooleanValue;
import javafx.collections.ListChangeListener;
import javafx.event.Event;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.Tooltip;
Expand All @@ -45,7 +42,6 @@
import org.jabref.gui.autosaveandbackup.BackupManager;
import org.jabref.gui.collab.DatabaseChangeMonitor;
import org.jabref.gui.dialogs.AutosaveUiManager;
import org.jabref.gui.entryeditor.EntryEditor;
import org.jabref.gui.exporter.SaveDatabaseAction;
import org.jabref.gui.externalfiles.ImportHandler;
import org.jabref.gui.fieldeditors.LinkedFileViewModel;
Expand All @@ -57,8 +53,6 @@
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.undo.CountingUndoManager;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.undo.RedoAction;
import org.jabref.gui.undo.UndoAction;
import org.jabref.gui.undo.UndoableFieldChange;
import org.jabref.gui.undo.UndoableInsertEntries;
import org.jabref.gui.undo.UndoableRemoveEntries;
Expand Down Expand Up @@ -91,7 +85,6 @@
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.entry.event.EntriesEventSource;
import org.jabref.model.entry.event.FieldChangedEvent;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.FieldFactory;
import org.jabref.model.groups.GroupTreeNode;
import org.jabref.model.search.query.SearchQuery;
Expand All @@ -112,11 +105,6 @@
* Represents the ui area where the notifier pane, the library table and the entry editor are shown.
*/
public class LibraryTab extends Tab {
/**
* Defines the different modes that the tab can operate in
*/
private enum PanelMode { MAIN_TABLE, MAIN_TABLE_AND_ENTRY_EDITOR }

private static final Logger LOGGER = LoggerFactory.getLogger(LibraryTab.class);
private final LibraryTabContainer tabContainer;
private final CountingUndoManager undoManager;
Expand All @@ -131,10 +119,7 @@ private enum PanelMode { MAIN_TABLE, MAIN_TABLE_AND_ENTRY_EDITOR }
private BibDatabaseContext bibDatabaseContext;
private MainTableDataModel tableModel;
private FileAnnotationCache annotationCache;
private EntryEditor entryEditor;
private MainTable mainTable;
private PanelMode mode = PanelMode.MAIN_TABLE;
private SplitPane splitPane;
private DatabaseNotification databaseNotificationPane;

// Indicates whether the tab is loading data using a dataloading task
Expand Down Expand Up @@ -225,7 +210,7 @@ private void initializeComponentsAndListeners(boolean isDummyContext) {
bibDatabaseContext.getMetaData().registerListener(this);

this.selectedGroupsProperty = new SimpleListProperty<>(stateManager.getSelectedGroups(bibDatabaseContext));
this.tableModel = new MainTableDataModel(getBibDatabaseContext(), preferences, taskExecutor, getIndexManager(), selectedGroupsProperty(), searchQueryProperty(), resultSizeProperty());
this.tableModel = new MainTableDataModel(getBibDatabaseContext(), preferences, taskExecutor, getIndexManager(), selectedGroupsProperty(), searchQueryProperty, resultSizeProperty());

new CitationStyleCache(bibDatabaseContext);
annotationCache = new FileAnnotationCache(bibDatabaseContext, preferences.getFilePreferences());
Expand All @@ -242,7 +227,6 @@ private void initializeComponentsAndListeners(boolean isDummyContext) {
setupAutoCompletion();

this.getDatabase().registerListener(new IndexUpdateListener());
this.getDatabase().registerListener(new EntriesRemovedListener());

// ensure that at each addition of a new entry, the entry is added to the groups interface
this.bibDatabaseContext.getDatabase().registerListener(new GroupTreeListener());
Expand All @@ -251,8 +235,6 @@ private void initializeComponentsAndListeners(boolean isDummyContext) {

this.getDatabase().registerListener(new UpdateTimestampListener(preferences));

this.entryEditor = createEntryEditor();

aiService.setupDatabase(bibDatabaseContext);

Platform.runLater(() -> {
Expand All @@ -262,14 +244,6 @@ private void initializeComponentsAndListeners(boolean isDummyContext) {
});
}

private EntryEditor createEntryEditor() {
Supplier<LibraryTab> tabSupplier = () -> this;
return new EntryEditor(this,
// Actions are recreated here since this avoids passing more parameters and the amount of additional memory consumption is neglegtable.
new UndoAction(tabSupplier, undoManager, dialogService, stateManager),
new RedoAction(tabSupplier, undoManager, dialogService, stateManager));
}

private static void addChangedInformation(StringBuilder text, String fileName) {
text.append("\n");
text.append(Localization.lang("Library '%0' has changed.", fileName));
Expand Down Expand Up @@ -477,14 +451,6 @@ public void registerUndoableChanges(List<FieldChange> changes) {
}
}

public void editEntryAndFocusField(BibEntry entry, Field field) {
showAndEdit(entry);
Platform.runLater(() -> {
// Focus field and entry in main table (async to give entry editor time to load)
entryEditor.setFocusToField(field);
});
}

private void createMainTable() {
mainTable = new MainTable(tableModel,
this,
Expand All @@ -498,34 +464,21 @@ private void createMainTable() {
entryTypesManager,
taskExecutor,
importHandler);

// Add the listener that binds selection to state manager (TODO: should be replaced by proper JavaFX binding as soon as table is implemented in JavaFX)
// content binding between StateManager#getselectedEntries and mainTable#getSelectedEntries does not work here as it does not trigger the ActionHelper#needsEntriesSelected checker for the menubar
mainTable.addSelectionListener(event -> {
List<BibEntry> entries = event.getList().stream().map(BibEntryTableViewModel::getEntry).toList();
stateManager.setSelectedEntries(entries);
if (!entries.isEmpty()) {
// Update entry editor and preview according to selected entries
entryEditor.setCurrentlyEditedEntry(entries.getFirst());
}
});
}

public void setupMainPanel() {
splitPane = new SplitPane();
splitPane.setOrientation(Orientation.VERTICAL);

createMainTable();

splitPane.getItems().add(mainTable);
databaseNotificationPane = new DatabaseNotification(splitPane);
databaseNotificationPane = new DatabaseNotification(mainTable);
setContent(databaseNotificationPane);

// Saves the divider position as soon as it changes
// We need to keep a reference to the subscription, otherwise the binding gets garbage collected
dividerPositionSubscription = EasyBind.valueAt(splitPane.getDividers(), 0)
.mapObservable(SplitPane.Divider::positionProperty)
.subscribeToValues(this::saveDividerLocation);

// Add changePane in case a file is present - otherwise just add the splitPane to the panel
Optional<Path> file = bibDatabaseContext.getDatabasePath();
if (file.isPresent()) {
Expand Down Expand Up @@ -561,37 +514,9 @@ public SuggestionProvider<Author> getAutoCompleter() {
return searchAutoCompleter;
}

public EntryEditor getEntryEditor() {
return entryEditor;
}

/**
* Sets the entry editor as the bottom component in the split pane. If an entry editor already was shown, makes sure that the divider doesn't move. Updates the mode to {@link PanelMode#MAIN_TABLE_AND_ENTRY_EDITOR}.
* Then shows the given entry.
*
* Additionally, selects the entry in the main table - so that the selected entry in the main table always corresponds to the edited entry.
*
* @param entry The entry to edit.
*/
public void showAndEdit(BibEntry entry) {
this.clearAndSelect(entry);
if (!splitPane.getItems().contains(entryEditor)) {
splitPane.getItems().addLast(entryEditor);
mode = PanelMode.MAIN_TABLE_AND_ENTRY_EDITOR;
splitPane.setDividerPositions(preferences.getEntryEditorPreferences().getDividerPosition());
}

// We use != instead of equals because of performance reasons
if (entry != showing) {
entryEditor.setCurrentlyEditedEntry(entry);
showing = entry;
}
entryEditor.requestFocus();
}

public void closeBottomPane() {
mode = PanelMode.MAIN_TABLE;
splitPane.getItems().remove(entryEditor);
stateManager.getEditorShowing().setValue(true);
}

/**
Expand All @@ -609,25 +534,6 @@ public void selectNextEntry() {
mainTable.getSelectionModel().clearAndSelect(mainTable.getSelectionModel().getSelectedIndex() + 1);
}

/**
* This method is called from an EntryEditor when it should be closed. We relay to the selection listener, which takes care of the rest.
*/
public void entryEditorClosing() {
closeBottomPane();
mainTable.requestFocus();
}

/**
* Closes the entry editor if it is showing any of the given entries.
*/
private void ensureNotShowingBottomPanel(List<BibEntry> entriesToCheck) {
// This method is not able to close the bottom pane currently

if ((mode == PanelMode.MAIN_TABLE_AND_ENTRY_EDITOR) && (entriesToCheck.contains(entryEditor.getCurrentlyEditedEntry()))) {
closeBottomPane();
}
}

/**
* Put an asterisk behind the filename to indicate the database has changed.
*/
Expand Down Expand Up @@ -676,15 +582,6 @@ private boolean showDeleteConfirmationDialog(int numberOfEntries) {
}
}

/**
* Depending on whether a preview or an entry editor is showing, save the current divider location in the correct preference setting.
*/
private void saveDividerLocation(Number position) {
if (mode == PanelMode.MAIN_TABLE_AND_ENTRY_EDITOR) {
preferences.getEntryEditorPreferences().setDividerPosition(position.doubleValue());
}
}

public boolean requestClose() {
if (bibDatabaseContext.getLocation() == DatabaseLocation.LOCAL) {
if (isModified()) {
Expand Down Expand Up @@ -889,6 +786,7 @@ public void insertEntries(final List<BibEntry> entries) {
importHandler.importCleanedEntries(entries);
getUndoManager().addEdit(new UndoableInsertEntries(bibDatabaseContext.getDatabase(), entries));
markBaseChanged();
stateManager.setSelectedEntries(entries);
if (preferences.getEntryEditorPreferences().shouldOpenOnNewEntry()) {
showAndEdit(entries.getFirst());
} else {
Expand Down Expand Up @@ -1014,7 +912,6 @@ private int doDeleteEntry(StandardActions mode, List<BibEntry> entries) {
}
}

ensureNotShowingBottomPanel(entries);
markBaseChanged();

// prevent the main table from loosing focus
Expand Down Expand Up @@ -1130,14 +1027,6 @@ public void listen(EntriesAddedEvent addedEntriesEvent) {
}
}

private class EntriesRemovedListener {

@Subscribe
public void listen(EntriesRemovedEvent entriesRemovedEvent) {
ensureNotShowingBottomPanel(entriesRemovedEvent.getBibEntries());
}
}

private class IndexUpdateListener {

@Subscribe
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/jabref/gui/LibraryTabContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.List;

import javafx.collections.ObservableList;

import org.jabref.model.database.BibDatabaseContext;

import org.jspecify.annotations.NonNull;
Expand All @@ -10,7 +12,7 @@

@NullMarked
public interface LibraryTabContainer {
List<LibraryTab> getLibraryTabs();
ObservableList<LibraryTab> getLibraryTabs();

@Nullable
LibraryTab getCurrentLibraryTab();
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/jabref/gui/StateManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
Expand Down Expand Up @@ -78,6 +80,7 @@ public class StateManager {
private final ObjectProperty<LastAutomaticFieldEditorEdit> lastAutomaticFieldEditorEdit = new SimpleObjectProperty<>();
private final ObservableList<String> searchHistory = FXCollections.observableArrayList();
private final List<AiChatWindow> aiChatWindows = new ArrayList<>();
private final BooleanProperty editorShowing = new SimpleBooleanProperty(false);

public ObservableList<SidePaneType> getVisibleSidePaneComponents() {
return visibleSidePanes;
Expand Down Expand Up @@ -231,4 +234,8 @@ public void clearSearchHistory() {
public List<AiChatWindow> getAiChatWindows() {
return aiChatWindows;
}

public BooleanProperty getEditorShowing() {
return editorShowing;
}
}
Loading
Loading