diff --git a/CHANGELOG.md b/CHANGELOG.md
index c942e0a1ed2..2b96f17b6c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,7 +26,8 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
- We added a feature that allows the user to open all linked files of multiple selected entries by "Open file" option. [#6966](https://github.com/JabRef/jabref/issues/6966)
- We added a keybinding preset for new entries. [#7705](https://github.com/JabRef/jabref/issues/7705)
- We added a select all button for the library import function. [#7786](https://github.com/JabRef/jabref/issues/7786)
-- We added auto-key-generation progress to the background task list. [#7267](https://github.com/JabRef/jabref/issues/7267)
+- We added a search feature for journal abbreviations. [#7804](https://github.com/JabRef/jabref/pull/7804)
+- We added auto-key-generation progress to the background task list. [#7267](https://github.com/JabRef/jabref/issues/72)
### Changed
diff --git a/src/main/java/org/jabref/gui/preferences/journals/AbbreviationViewModel.java b/src/main/java/org/jabref/gui/preferences/journals/AbbreviationViewModel.java
index 26bf67cb1ec..b8f294fb06a 100644
--- a/src/main/java/org/jabref/gui/preferences/journals/AbbreviationViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/journals/AbbreviationViewModel.java
@@ -1,5 +1,6 @@
package org.jabref.gui.preferences.journals;
+import java.util.Locale;
import java.util.Objects;
import javafx.beans.property.BooleanProperty;
@@ -94,4 +95,11 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(getName(), isPseudoAbbreviation());
}
+
+ public boolean containsCaseIndependent(String searchTerm) {
+ searchTerm = searchTerm.toLowerCase(Locale.ROOT);
+ return this.abbreviation.get().toLowerCase(Locale.ROOT).contains(searchTerm) ||
+ this.name.get().toLowerCase(Locale.ROOT).contains(searchTerm) ||
+ this.shortestUniqueAbbreviation.get().toLowerCase(Locale.ROOT).contains(searchTerm);
+ }
}
diff --git a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml
index 3f4a342f534..0dc1c91d3f0 100644
--- a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml
+++ b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.fxml
@@ -10,6 +10,8 @@
+
+
@@ -64,4 +66,9 @@
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java
index 0db240a26e9..c89d1c0e11c 100644
--- a/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java
+++ b/src/main/java/org/jabref/gui/preferences/journals/JournalAbbreviationsTab.java
@@ -2,6 +2,16 @@
import javax.inject.Inject;
+import javafx.animation.Interpolator;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.collections.transformation.FilteredList;
+import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
@@ -10,16 +20,20 @@
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
+import javafx.scene.paint.Color;
+import javafx.util.Duration;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.preferences.AbstractPreferenceTabView;
import org.jabref.gui.preferences.PreferencesTab;
+import org.jabref.gui.util.ColorUtil;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.l10n.Localization;
import com.airhacks.afterburner.views.ViewLoader;
import com.tobiasdiez.easybind.EasyBind;
+import org.controlsfx.control.textfield.CustomTextField;
/**
* This class controls the user interface of the journal abbreviations dialog. The UI elements and their layout are
@@ -33,6 +47,7 @@ public class JournalAbbreviationsTab extends AbstractPreferenceTabView journalTableNameColumn;
@FXML private TableColumn journalTableAbbreviationColumn;
@FXML private TableColumn journalTableShortestUniqueAbbreviationColumn;
+ private FilteredList filteredAbbreviations;
@FXML private ComboBox journalFilesBox;
@FXML private Button addAbbreviationButton;
@FXML private Button removeAbbreviationButton;
@@ -40,9 +55,15 @@ public class JournalAbbreviationsTab extends AbstractPreferenceTabView flashingColor;
+ private StringProperty flashingColorStringProperty;
+
public JournalAbbreviationsTab() {
ViewLoader.view(this)
.root(this)
@@ -53,9 +74,15 @@ public JournalAbbreviationsTab() {
private void initialize() {
viewModel = new JournalAbbreviationsTabViewModel(preferencesService, dialogService, taskExecutor, abbreviationRepository);
+ filteredAbbreviations = new FilteredList<>(viewModel.abbreviationsProperty());
+
setButtonStyles();
setUpTable();
setBindings();
+ setAnimations();
+
+ searchBox.setPromptText(Localization.lang("Search") + "...");
+ searchBox.setLeft(IconTheme.JabRefIcons.SEARCH.getGraphicNode());
}
private void setButtonStyles() {
@@ -78,7 +105,7 @@ private void setUpTable() {
}
private void setBindings() {
- journalAbbreviationsTable.itemsProperty().bindBidirectional(viewModel.abbreviationsProperty());
+ journalAbbreviationsTable.setItems(filteredAbbreviations);
EasyBind.subscribe(journalAbbreviationsTable.getSelectionModel().selectedItemProperty(), newValue ->
viewModel.currentAbbreviationProperty().set(newValue));
@@ -98,6 +125,27 @@ private void setBindings() {
loadingLabel.visibleProperty().bind(viewModel.isLoadingProperty());
progressIndicator.visibleProperty().bind(viewModel.isLoadingProperty());
+
+ searchBox.textProperty().addListener((observable, previousText, searchTerm) -> {
+ filteredAbbreviations.setPredicate(abbreviation -> searchTerm.isEmpty() ? true : abbreviation.containsCaseIndependent(searchTerm));
+ });
+ }
+
+ private void setAnimations() {
+ flashingColor = new SimpleObjectProperty<>(Color.TRANSPARENT);
+ flashingColorStringProperty = createFlashingColorStringProperty(flashingColor);
+ searchBox.styleProperty().bind(
+ new SimpleStringProperty("-fx-control-inner-background: ").concat(flashingColorStringProperty).concat(";")
+ );
+ invalidateSearch = new Timeline(
+ new KeyFrame(Duration.seconds(0), new KeyValue(flashingColor, Color.TRANSPARENT, Interpolator.LINEAR)),
+ new KeyFrame(Duration.seconds(0.25), new KeyValue(flashingColor, Color.RED, Interpolator.LINEAR)),
+ new KeyFrame(Duration.seconds(0.25), new KeyValue(searchBox.textProperty(), "", Interpolator.DISCRETE)),
+ new KeyFrame(Duration.seconds(0.25), (ActionEvent event) -> {
+ addAbbreviationActions();
+ }),
+ new KeyFrame(Duration.seconds(0.5), new KeyValue(flashingColor, Color.TRANSPARENT, Interpolator.LINEAR))
+ );
}
@FXML
@@ -117,11 +165,30 @@ private void removeList() {
@FXML
private void addAbbreviation() {
+ if (!searchBox.getText().isEmpty()) {
+ invalidateSearch.play();
+ } else {
+ addAbbreviationActions();
+ }
+ }
+
+ private void addAbbreviationActions() {
viewModel.addAbbreviation();
selectNewAbbreviation();
editAbbreviation();
}
+ private static StringProperty createFlashingColorStringProperty(final ObjectProperty flashingColor) {
+ final StringProperty flashingColorStringProperty = new SimpleStringProperty();
+ setColorStringFromColor(flashingColorStringProperty, flashingColor);
+ flashingColor.addListener((observable, oldValue, newValue) -> setColorStringFromColor(flashingColorStringProperty, flashingColor));
+ return flashingColorStringProperty;
+ }
+
+ private static void setColorStringFromColor(StringProperty colorStringProperty, ObjectProperty color) {
+ colorStringProperty.set(ColorUtil.toRGBACode(color.get()));
+ }
+
@FXML
private void editAbbreviation() {
journalAbbreviationsTable.edit(
@@ -138,7 +205,7 @@ private void selectNewAbbreviation() {
int lastRow = viewModel.abbreviationsCountProperty().get() - 1;
journalAbbreviationsTable.scrollTo(lastRow);
journalAbbreviationsTable.getSelectionModel().select(lastRow);
- journalAbbreviationsTable.getFocusModel().focus(lastRow);
+ journalAbbreviationsTable.getFocusModel().focus(lastRow, journalTableNameColumn);
}
@Override
diff --git a/src/main/java/org/jabref/gui/util/ColorUtil.java b/src/main/java/org/jabref/gui/util/ColorUtil.java
index 19a8be2fd52..326c7f0f27d 100644
--- a/src/main/java/org/jabref/gui/util/ColorUtil.java
+++ b/src/main/java/org/jabref/gui/util/ColorUtil.java
@@ -11,6 +11,14 @@ public static String toRGBCode(Color color) {
(int) (color.getBlue() * 255));
}
+ public static String toRGBACode(Color color) {
+ return String.format("rgba(%d,%d,%d,%f)",
+ (int) (color.getRed() * 255),
+ (int) (color.getGreen() * 255),
+ (int) (color.getBlue() * 255),
+ color.getOpacity());
+ }
+
public static String toHex(Color validFieldBackgroundColor) {
return String.format("#%02x%02x%02x", (int) validFieldBackgroundColor.getRed(), (int) validFieldBackgroundColor.getGreen(), (int) validFieldBackgroundColor.getBlue());
}
diff --git a/src/test/java/org/jabref/gui/preferences/journals/AbbreviationViewModelTest.java b/src/test/java/org/jabref/gui/preferences/journals/AbbreviationViewModelTest.java
new file mode 100644
index 00000000000..87f9ea5aeab
--- /dev/null
+++ b/src/test/java/org/jabref/gui/preferences/journals/AbbreviationViewModelTest.java
@@ -0,0 +1,44 @@
+package org.jabref.gui.preferences.journals;
+
+import java.util.stream.Stream;
+
+import org.jabref.logic.journals.Abbreviation;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class AbbreviationViewModelTest {
+
+ @ParameterizedTest
+ @MethodSource("provideContainsCaseIndependentContains")
+ void containsCaseIndependentContains(String searchTerm, AbbreviationViewModel abbreviation) {
+ assertTrue(abbreviation.containsCaseIndependent(searchTerm));
+ }
+
+ private static Stream provideContainsCaseIndependentContains() {
+ return Stream.of(
+ Arguments.of("name", new AbbreviationViewModel(new Abbreviation("Long Name", "abbr", "unique"))),
+ Arguments.of("bBr", new AbbreviationViewModel(new Abbreviation("Long Name", "abbr", "unique"))),
+ Arguments.of("Uniq", new AbbreviationViewModel(new Abbreviation("Long Name", "abbr", "unique"))),
+ Arguments.of("", new AbbreviationViewModel(new Abbreviation("Long Name", "abbr", "unique"))),
+ Arguments.of("", new AbbreviationViewModel(new Abbreviation("", "", "")))
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideContainsCaseIndependentDoesNotContain")
+ void containsCaseIndependentDoesNotContain(String searchTerm, AbbreviationViewModel abbreviation) {
+ assertFalse(abbreviation.containsCaseIndependent(searchTerm));
+ }
+
+ private static Stream provideContainsCaseIndependentDoesNotContain() {
+ return Stream.of(
+ Arguments.of("Something else", new AbbreviationViewModel(new Abbreviation("Long Name", "abbr", "unique"))),
+ Arguments.of("Something", new AbbreviationViewModel(new Abbreviation("", "", "")))
+ );
+ }
+}
diff --git a/src/test/java/org/jabref/gui/util/ColorUtilTest.java b/src/test/java/org/jabref/gui/util/ColorUtilTest.java
index a5f3a552841..a04489ac908 100644
--- a/src/test/java/org/jabref/gui/util/ColorUtilTest.java
+++ b/src/test/java/org/jabref/gui/util/ColorUtilTest.java
@@ -1,26 +1,51 @@
package org.jabref.gui.util;
+import java.util.stream.Stream;
+
import javafx.scene.paint.Color;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ColorUtilTest {
+ private static final Color C1 = Color.color(0.2, 0.4, 1);
+ private static final Color C2 = Color.rgb(255, 255, 255);
+ private static final Color C3 = Color.color(0, 0, 0, 0);
+ private static final Color C4 = Color.color(1, 1, 1, 1);
+ private static final Color C5 = Color.color(0.6, 0.8, 0.5, 0.3);
+
private ColorUtil colorUtil = new ColorUtil();
- private final Color c1 = Color.color(0.2, 0.4, 1);
- private final Color c2 = Color.rgb(255, 255, 255);
@Test
public void toRGBCodeTest() {
- assertEquals("#3366FF", ColorUtil.toRGBCode(c1));
- assertEquals("#FFFFFF", ColorUtil.toRGBCode(c2));
+ assertEquals("#3366FF", ColorUtil.toRGBCode(C1));
+ assertEquals("#FFFFFF", ColorUtil.toRGBCode(C2));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideToRGBACodeTest")
+ public void toRGBACodeTest(Color color, String expected) {
+ assertEquals(expected, ColorUtil.toRGBACode(color));
+ }
+
+ private static Stream provideToRGBACodeTest() {
+ return Stream.of(
+ Arguments.of(C1, String.format("rgba(51,102,255,%f)", 1.0)),
+ Arguments.of(C2, String.format("rgba(255,255,255,%f)", 1.0)),
+ Arguments.of(C3, String.format("rgba(0,0,0,%f)", 0.0)),
+ Arguments.of(C4, String.format("rgba(255,255,255,%f)", 1.0)),
+ Arguments.of(C5, String.format("rgba(153,204,127,%f)", 0.3))
+ );
}
@Test
public void toHexTest() {
- assertEquals("#000001", ColorUtil.toHex(c1));
- assertEquals("#010101", ColorUtil.toHex(c2));
+ assertEquals("#000001", ColorUtil.toHex(C1));
+ assertEquals("#010101", ColorUtil.toHex(C2));
}
}