Skip to content

Commit

Permalink
Merge pull request #5137 from davidemdot/latexintegration-entryeditortab
Browse files Browse the repository at this point in the history
Add LaTeX citations tab to the entry editor
  • Loading branch information
davidemdot committed Jul 29, 2019
2 parents 278f362 + 34713f5 commit 7a49dd2
Show file tree
Hide file tree
Showing 29 changed files with 568 additions and 243 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
- We moved the dropdown menu for selecting the push-application from the toolbar into the external application preferences. [#674](https://github.com/JabRef/jabref/issues/674)
- We removed the alphabetical ordering of the custom tabs and updated the error message when trying to create a general field with a name containing an illegal character. [#5019](https://github.com/JabRef/jabref/issues/5019)
- We added a context menu to the bib(la)tex-source-editor to copy'n'paste. [#5007](https://github.com/JabRef/jabref/pull/5007)
- We added a bibliographic references search, for finding references in several LaTeX files. This tool scans directories and shows which entries are used, how many times and where.
- We added a tool that allows searching for citations in LaTeX files. It scans directories and shows which entries are used, how many times and where.
- We added a 'LaTeX citations' tab to the entry editor, to search for citations to the active entry in the LaTeX file directory. It can be disabled in the preferences dialog.
- We added an option in preferences to allow for integers in field "edition" when running database in bibtex mode. [#4680](https://github.com/JabRef/jabref/issues/4680)


Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/BasePanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas

this.getDatabase().registerListener(new UpdateTimestampListener(Globals.prefs));

this.entryEditor = new EntryEditor(this, preferences.getEntryEditorPreferences(), Globals.getFileUpdateMonitor(), dialogService, externalFileTypes, Globals.TASK_EXECUTOR, Globals.stateManager);
this.entryEditor = new EntryEditor(this, externalFileTypes);

this.preview = new PreviewPanel(getBibDatabaseContext(), this, dialogService, externalFileTypes, Globals.getKeyPrefs(), preferences.getPreviewPreferences());
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/actions/StandardActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public enum StandardActions implements Action {
TOOGLE_OO(Localization.lang("OpenOffice/LibreOffice"), IconTheme.JabRefIcons.FILE_OPENOFFICE, KeyBinding.OPEN_OPEN_OFFICE_LIBRE_OFFICE_CONNECTION),
TOGGLE_WEB_SEARCH(Localization.lang("Web search"), Localization.lang("Toggle web search interface"), IconTheme.JabRefIcons.WWW, KeyBinding.WEB_SEARCH),

PARSE_TEX(Localization.lang("Search for Citations in LaTeX Files"), IconTheme.JabRefIcons.APPLICATION_TEXSTUDIO),
PARSE_TEX(Localization.lang("Search for Citations in LaTeX Files"), IconTheme.JabRefIcons.LATEX_CITATIONS),
NEW_SUB_LIBRARY_FROM_AUX(Localization.lang("New sublibrary based on AUX file") + "...", Localization.lang("New BibTeX sublibrary") + Localization.lang("This feature generates a new library based on which entries are needed in an existing LaTeX document."), IconTheme.JabRefIcons.NEW),
WRITE_XMP(Localization.lang("Write XMP-metadata to PDFs"), Localization.lang("Will write XMP-metadata to the PDFs linked from selected entries."), KeyBinding.WRITE_XMP),
OPEN_FOLDER(Localization.lang("Open folder"), Localization.lang("Open folder"), KeyBinding.OPEN_FOLDER),
Expand Down
146 changes: 76 additions & 70 deletions src/main/java/org/jabref/gui/entryeditor/EntryEditor.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public class EntryEditorPreferences {
private boolean showSourceTabByDefault;
private final KeyBindingRepository keyBindings;
private boolean avoidOverwritingCiteKey;
private final boolean shouldShowLatexCitationsTab;

public EntryEditorPreferences(Map<String, List<String>> entryEditorTabList, LatexFieldFormatterPreferences latexFieldFormatterPreferences, ImportFormatPreferences importFormatPreferences, List<String> customTabFieldNames, boolean shouldShowRecommendationsTab, boolean isMrdlibAccepted, boolean showSourceTabByDefault, BibtexKeyPatternPreferences bibtexKeyPatternPreferences, KeyBindingRepository keyBindings, boolean avoidOverwritingCiteKey) {
public EntryEditorPreferences(Map<String, List<String>> entryEditorTabList, LatexFieldFormatterPreferences latexFieldFormatterPreferences, ImportFormatPreferences importFormatPreferences, List<String> customTabFieldNames, boolean shouldShowRecommendationsTab, boolean isMrdlibAccepted, boolean shouldShowLatexCitationsTab, boolean showSourceTabByDefault, BibtexKeyPatternPreferences bibtexKeyPatternPreferences, KeyBindingRepository keyBindings, boolean avoidOverwritingCiteKey) {
this.entryEditorTabList = entryEditorTabList;
this.latexFieldFormatterPreferences = latexFieldFormatterPreferences;
this.importFormatPreferences = importFormatPreferences;
Expand All @@ -32,6 +33,7 @@ public EntryEditorPreferences(Map<String, List<String>> entryEditorTabList, Late
this.bibtexKeyPatternPreferences = bibtexKeyPatternPreferences;
this.keyBindings = keyBindings;
this.avoidOverwritingCiteKey = avoidOverwritingCiteKey;
this.shouldShowLatexCitationsTab = shouldShowLatexCitationsTab;
}

public Map<String, List<String>> getEntryEditorTabList() {
Expand Down Expand Up @@ -78,6 +80,10 @@ public void setShowSourceTabByDefault(boolean showSourceTabByDefault) {
this.showSourceTabByDefault = showSourceTabByDefault;
}

public boolean shouldShowLatexCitationsTab() {
return shouldShowLatexCitationsTab;
}

public boolean avoidOverwritingCiteKey() {
return avoidOverwritingCiteKey;
}
Expand Down
122 changes: 122 additions & 0 deletions src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package org.jabref.gui.entryeditor;

import java.nio.file.Path;
import java.util.Optional;
import java.util.stream.Collectors;

import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;

import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.preferences.PreferencesService;

import org.fxmisc.easybind.EasyBind;

public class LatexCitationsTab extends EntryEditorTab {

private final LatexCitationsTabViewModel viewModel;
private final StackPane searchPane;
private final ProgressIndicator progressIndicator;

public LatexCitationsTab(BibDatabaseContext databaseContext, PreferencesService preferencesService,
TaskExecutor taskExecutor) {
this.viewModel = new LatexCitationsTabViewModel(databaseContext, preferencesService, taskExecutor);
this.searchPane = new StackPane();
this.progressIndicator = new ProgressIndicator();

setText(Localization.lang("LaTeX Citations"));
setTooltip(new Tooltip(Localization.lang("Search citations for this entry in LaTeX files")));
setGraphic(IconTheme.JabRefIcons.LATEX_CITATIONS.getGraphicNode());
setSearchPane();
}

private void setSearchPane() {
progressIndicator.setMaxSize(100, 100);
searchPane.getStyleClass().add("related-articles-tab");

setContent(searchPane);

EasyBind.subscribe(viewModel.statusProperty(), status -> {
switch (status) {
case IN_PROGRESS:
searchPane.getChildren().setAll(progressIndicator);
break;
case CITATIONS_FOUND:
searchPane.getChildren().setAll(getCitationsPane());
break;
case NO_RESULTS:
searchPane.getChildren().setAll(getNotFoundPane());
break;
case ERROR:
searchPane.getChildren().setAll(getErrorPane());
break;
}
});
}

private ScrollPane getCitationsPane() {
Text titleText = new Text(Localization.lang("Citations found"));
titleText.getStyleClass().add("recommendation-heading");

VBox citationsBox = new VBox(20, titleText);
Path basePath = viewModel.directoryProperty().get();
citationsBox.getChildren().addAll(viewModel.getCitationList().stream().map(
citation -> citation.getDisplayGraphic(basePath, Optional.empty())).collect(Collectors.toList()));

ScrollPane citationsPane = new ScrollPane();
citationsPane.setContent(citationsBox);

return citationsPane;
}

private ScrollPane getNotFoundPane() {
Text notFoundTitleText = new Text(Localization.lang("No citations found"));
notFoundTitleText.getStyleClass().add("recommendation-heading");

Text notFoundText = new Text(Localization.lang("No LaTeX files containing this entry were found."));
notFoundText.setStyle("-fx-font-size: 110%");

Text notFoundAdviceText = new Text(Localization.lang(
"You can set the LaTeX file directory in the 'Library properties' dialog."));
notFoundAdviceText.setStyle("-fx-font-weight: bold;");

VBox notFoundBox = new VBox(20, notFoundTitleText, notFoundText, notFoundAdviceText);
ScrollPane notFoundPane = new ScrollPane();
notFoundPane.setContent(notFoundBox);

return notFoundPane;
}

private ScrollPane getErrorPane() {
Text errorTitleText = new Text(Localization.lang("Error"));
errorTitleText.setStyle("-fx-fill: -fx-accent;");
errorTitleText.getStyleClass().add("recommendation-heading");

Text errorMessageText = new Text(viewModel.searchErrorProperty().get());
errorMessageText.setStyle("-fx-font-family: monospace;-fx-font-size: 120%;");

VBox errorBox = new VBox(20, errorTitleText, errorMessageText);
ScrollPane errorPane = new ScrollPane();
errorPane.setContent(errorBox);

return errorPane;
}

@Override
protected void bindToEntry(BibEntry entry) {
viewModel.init(entry);
}

@Override
public boolean shouldShow(BibEntry entry) {
return viewModel.shouldShow();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package org.jabref.gui.entryeditor;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyListWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.texparser.CitationViewModel;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.texparser.DefaultTexParser;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.texparser.TexParserResult;
import org.jabref.preferences.PreferencesService;

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

public class LatexCitationsTabViewModel extends AbstractViewModel {

enum Status {
IN_PROGRESS,
CITATIONS_FOUND,
NO_RESULTS,
ERROR
}

private static final Logger LOGGER = LoggerFactory.getLogger(LatexCitationsTabViewModel.class);
private static final String TEX_EXT = ".tex";
private final BibDatabaseContext databaseContext;
private final PreferencesService preferencesService;
private final TaskExecutor taskExecutor;
private final ObjectProperty<Path> directory;
private final ObservableList<CitationViewModel> citationList;
private final ObjectProperty<Status> status;
private final StringProperty searchError;
private Future<?> searchTask;

public LatexCitationsTabViewModel(BibDatabaseContext databaseContext, PreferencesService preferencesService,
TaskExecutor taskExecutor) {
this.databaseContext = databaseContext;
this.preferencesService = preferencesService;
this.taskExecutor = taskExecutor;
this.directory = new SimpleObjectProperty<>(null);
this.citationList = FXCollections.observableArrayList();
this.status = new SimpleObjectProperty<>(Status.IN_PROGRESS);
this.searchError = new SimpleStringProperty("");
}

public void init(BibEntry entry) {
cancelSearch();

Optional<String> citeKey = entry.getCiteKeyOptional();
if (citeKey.isPresent()) {
startSearch(citeKey.get());
} else {
searchError.set(Localization.lang("Selected entry does not have an associated BibTeX key."));
status.set(Status.ERROR);
}
}

public ObjectProperty<Path> directoryProperty() {
return directory;
}

public ObservableList<CitationViewModel> getCitationList() {
return new ReadOnlyListWrapper<>(citationList);
}

public ObjectProperty<Status> statusProperty() {
return status;
}

public StringProperty searchErrorProperty() {
return searchError;
}

private void startSearch(String citeKey) {
searchTask = BackgroundTask.wrap(() -> searchAndParse(citeKey))
.onRunning(() -> status.set(Status.IN_PROGRESS))
.onSuccess(status::set)
.onFailure(error -> {
searchError.set(String.format("%s%n%n%s", error.getMessage(), error.getCause()));
status.set(Status.ERROR);
})
.executeWith(taskExecutor);
}

private void cancelSearch() {
if (searchTask == null || searchTask.isCancelled() || searchTask.isDone()) {
return;
}

status.set(Status.IN_PROGRESS);
searchTask.cancel(true);
}

private Status searchAndParse(String citeKey) throws IOException {
directory.set(databaseContext.getMetaData().getLaTexFileDirectory(preferencesService.getUser())
.orElseGet(preferencesService::getWorkingDir));
List<Path> texFiles;
try (Stream<Path> filesStream = Files.walk(directory.get())) {
texFiles = filesStream.filter(path -> path.toFile().isFile() && path.toString().endsWith(TEX_EXT))
.collect(Collectors.toList());
} catch (IOException e) {
LOGGER.error("Error searching files", e);
throw new IOException("Error searching files", e);
}

TexParserResult texParserResult = new DefaultTexParser().parse(citeKey, texFiles);
citationList.setAll(texParserResult.getCitations().values().stream().map(CitationViewModel::new).collect(Collectors.toList()));

return citationList.isEmpty() ? Status.NO_RESULTS : Status.CITATIONS_FOUND;
}

public boolean shouldShow() {
return preferencesService.getEntryEditorPreferences().shouldShowLatexCitationsTab();
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/jabref/gui/icon/IconTheme.java
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@ public enum JabRefIcons implements JabRefIcon {
OWNER(MaterialDesignIcon.ACCOUNT),
CLOSE_JABREF(MaterialDesignIcon.GLASSDOOR),
ARTICLE(MaterialDesignIcon.FILE_DOCUMENT),
BOOK(MaterialDesignIcon.BOOK_OPEN_PAGE_VARIANT);
BOOK(MaterialDesignIcon.BOOK_OPEN_PAGE_VARIANT),
LATEX_CITATIONS(JabRefMaterialDesignIcon.TEX_STUDIO);

private final JabRefIcon icon;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class EntryEditorPrefsTab extends Pane implements PrefsTab {
private final CheckBox autoComplete;
private final CheckBox recommendations;
private final CheckBox acceptRecommendations;
private final CheckBox latexCitations;
private final CheckBox validation;
private final RadioButton autoCompBoth;
private final RadioButton autoCompFF;
Expand Down Expand Up @@ -60,6 +61,7 @@ public EntryEditorPrefsTab(JabRefPreferences prefs) {
autoComplete = new CheckBox(Localization.lang("Enable word/name autocompletion"));
recommendations = new CheckBox(Localization.lang("Show 'Related Articles' tab"));
acceptRecommendations = new CheckBox(Localization.lang("Accept recommendations from Mr. DLib"));
latexCitations = new CheckBox(Localization.lang("Show 'LaTeX Citations' tab"));
validation = new CheckBox(Localization.lang("Show validation messages"));

// allowed name formats
Expand Down Expand Up @@ -96,8 +98,9 @@ public EntryEditorPrefsTab(JabRefPreferences prefs) {
builder.add(emacsRebindCtrlF, 1, 6);
builder.add(recommendations, 1, 7);
builder.add(acceptRecommendations, 1, 8);
builder.add(validation, 1, 9);
builder.add(new Label(""), 1, 10);
builder.add(latexCitations, 1, 9);
builder.add(validation, 1, 10);
builder.add(new Label(""), 1, 11);

builder.add(new Separator(), 1, 13);

Expand Down Expand Up @@ -189,6 +192,7 @@ public void setValues() {
emacsRebindCtrlF.setSelected(prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS_REBIND_CF));
recommendations.setSelected(prefs.getBoolean(JabRefPreferences.SHOW_RECOMMENDATIONS));
acceptRecommendations.setSelected(prefs.getBoolean(JabRefPreferences.ACCEPT_RECOMMENDATIONS));
latexCitations.setSelected(prefs.getBoolean(JabRefPreferences.SHOW_LATEX_CITATIONS));
autoComplete.setSelected(autoCompletePreferences.shouldAutoComplete());
autoCompFields.setText(autoCompletePreferences.getCompleteNamesAsString());

Expand Down Expand Up @@ -235,6 +239,7 @@ public void storeSettings() {
prefs.putBoolean(JabRefPreferences.DEFAULT_SHOW_SOURCE, defSource.isSelected());
prefs.putBoolean(JabRefPreferences.SHOW_RECOMMENDATIONS, recommendations.isSelected());
prefs.putBoolean(JabRefPreferences.ACCEPT_RECOMMENDATIONS, acceptRecommendations.isSelected());
prefs.putBoolean(JabRefPreferences.SHOW_LATEX_CITATIONS, latexCitations.isSelected());
prefs.putBoolean(JabRefPreferences.VALIDATE_IN_ENTRY_EDITOR, validation.isSelected());
boolean emacsModeChanged = prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS) != emacsMode.isSelected();
boolean emacsRebindCtrlAChanged = prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS_REBIND_CA) != emacsRebindCtrlA.isSelected();
Expand Down
Loading

0 comments on commit 7a49dd2

Please sign in to comment.