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

Refactoring: Lazy init of all editor tabs #3333

Merged
merged 5 commits into from
Oct 23, 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
15 changes: 11 additions & 4 deletions src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java
Original file line number Diff line number Diff line change
@@ -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<String> determineFieldsToShow(BibEntry entry, EntryType entryType) {
return entryType.getDeprecatedFields();
}
}
168 changes: 66 additions & 102 deletions src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -152,14 +148,14 @@ public class EntryEditor extends JPanel implements EntryContainer {
private final RedoAction redoAction = new RedoAction();
private final List<SearchQueryHighlightListener> searchListeners = new ArrayList<>();
private final JFXPanel container;
private final List<EntryEditorTab> tabs;

/**
* Indicates that we are about to go to the next or previous entry
*/
private final BooleanProperty movingToDifferentEntry = new SimpleBooleanProperty();
private EntryType entryType;
private SourceTab sourceTab;
private final BorderLayout layout;
private TypeLabel typeLabel;

public EntryEditor(BasePanel panel) {
Expand All @@ -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) {
Expand All @@ -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));
}

/**
Expand Down Expand Up @@ -335,53 +293,62 @@ public void close() {
closeAction.actionPerformed(null);
}

private void addTabs(String lastTabName) {
private void recalculateVisibleTabs() {
List<Tab> visibleTabs = tabs.stream().filter(tab -> tab.shouldShow(entry)).collect(Collectors.toList());

List<EntryEditorTab> 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<EntryEditorTab> 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<EntryEditorTab> createTabs() {
List<EntryEditorTab> 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() {
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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));
}
}

Expand Down
34 changes: 22 additions & 12 deletions src/main/java/org/jabref/gui/entryeditor/EntryEditorTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Loading