Skip to content

Commit 7db3b81

Browse files
committed
Merge branch 'main' of https://github.com/JabRef/jabref into fix-for-issue-JabRef#12272
2 parents cdc1977 + f328ebf commit 7db3b81

32 files changed

+366
-11
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
1111

1212
### Added
1313

14+
- We added a new functionality that displays a drop-down list of matching suggestions when typing a citation key pattern. [#12502](https://github.com/JabRef/jabref/issues/12502)
1415
- We added a new CLI that supports txt, csv, and console-based output for consistency in BibTeX entries. [#11984](https://github.com/JabRef/jabref/issues/11984)
1516
- We added a new dialog for bibliography consistency check. [#11950](https://github.com/JabRef/jabref/issues/11950)
1617
- We added a feature for copying entries to libraries, available via the context menu, with an option to include cross-references. [#12374](https://github.com/JabRef/jabref/pull/12374)
@@ -29,6 +30,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2930

3031
### Fixed
3132

33+
- We fixed an issue where the file renaming dialog was not resizable and its size was too small for long file names. [#12518](https://github.com/JabRef/jabref/pull/12518)
3234
- We fixed an issue where the name of the untitled database was shown as a blank space in the right-click context menu's "Copy to" option. [#12459](https://github.com/JabRef/jabref/pull/12459)
3335
- We fixed an issue where the F3 shortcut key did not work without opening the right-click context menu. [#12417](https://github.com/JabRef/jabref/pull/12417)
3436
- We fixed an issue where a bib file with UFF-8 charset was wrongly loaded with a different charset [forum#5369](https://discourse.jabref.org/t/jabref-5-15-opens-bib-files-with-shift-jis-encoding-instead-of-utf-8/5369/)

src/main/java/org/jabref/gui/JabRefDialogService.java

+2
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ public Optional<String> showInputDialogWithDefaultAndWait(String title, String c
178178
inputDialog.setHeaderText(title);
179179
inputDialog.setContentText(content);
180180
inputDialog.initOwner(mainWindow);
181+
inputDialog.getDialogPane().setPrefSize(500, 200);
182+
inputDialog.setResizable(true);
181183
return inputDialog.showAndWait();
182184
}
183185

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package org.jabref.gui.commonfxcontrols;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.stream.Collectors;
7+
8+
import javafx.scene.control.ContextMenu;
9+
import javafx.scene.control.CustomMenuItem;
10+
import javafx.scene.control.Label;
11+
import javafx.scene.control.Menu;
12+
import javafx.scene.control.MenuItem;
13+
import javafx.scene.control.TextField;
14+
import javafx.scene.control.cell.TextFieldTableCell;
15+
16+
import org.jabref.logic.citationkeypattern.CitationKeyPattern;
17+
import org.jabref.logic.l10n.Localization;
18+
19+
public class CitationKeyPatternSuggestionCell extends TextFieldTableCell<CitationKeyPatternsPanelItemModel, String> {
20+
private final CitationKeyPatternSuggestionTextField searchField;
21+
22+
public CitationKeyPatternSuggestionCell(List<String> citationKeyPatterns) {
23+
this.searchField = new CitationKeyPatternSuggestionTextField(citationKeyPatterns);
24+
searchField.setOnAction(event -> commitEdit(searchField.getText()));
25+
}
26+
27+
@Override
28+
public void startEdit() {
29+
super.startEdit();
30+
searchField.setText(getItem());
31+
setGraphic(searchField);
32+
searchField.requestFocus();
33+
}
34+
35+
@Override
36+
public void cancelEdit() {
37+
super.cancelEdit();
38+
setGraphic(null);
39+
}
40+
41+
@Override
42+
public void commitEdit(String newValue) {
43+
super.commitEdit(newValue);
44+
getTableView().getItems().get(getIndex()).setPattern(newValue);
45+
setGraphic(null);
46+
}
47+
48+
@Override
49+
public void updateItem(String item, boolean empty) {
50+
super.updateItem(item, empty);
51+
if (empty || item == null) {
52+
setGraphic(null);
53+
setText(null);
54+
} else {
55+
setText(item);
56+
}
57+
}
58+
59+
static class CitationKeyPatternSuggestionTextField extends TextField {
60+
// Maximum number of entries that can be displayed in the popup menu.
61+
private static final int MAX_ENTRIES = 7;
62+
63+
private final List<String> citationKeyPatterns;
64+
private final ContextMenu suggestionsList;
65+
66+
public CitationKeyPatternSuggestionTextField(List<String> citationKeyPatterns) {
67+
this.citationKeyPatterns = new ArrayList<>(citationKeyPatterns);
68+
this.suggestionsList = new ContextMenu();
69+
70+
setListener();
71+
}
72+
73+
private void setListener() {
74+
textProperty().addListener((observable, oldValue, newValue) -> {
75+
String enteredText = getText();
76+
if (enteredText == null || enteredText.isEmpty()) {
77+
suggestionsList.hide();
78+
} else {
79+
List<String> filteredEntries = citationKeyPatterns.stream()
80+
.filter(e -> e.toLowerCase().contains(enteredText.toLowerCase()))
81+
.collect(Collectors.toList());
82+
83+
if (!filteredEntries.isEmpty()) {
84+
populatePopup(filteredEntries);
85+
if (!suggestionsList.isShowing() && getScene() != null) {
86+
double screenX = localToScreen(0, 0).getX();
87+
double screenY = localToScreen(0, 0).getY() + getHeight();
88+
suggestionsList.show(this, screenX, screenY);
89+
}
90+
} else {
91+
suggestionsList.hide();
92+
}
93+
}
94+
});
95+
96+
focusedProperty().addListener((observable, oldValue, newValue) -> {
97+
suggestionsList.hide();
98+
});
99+
}
100+
101+
private void populatePopup(List<String> searchResult) {
102+
List<CustomMenuItem> menuItems = new ArrayList<>();
103+
int count = Math.min(searchResult.size(), MAX_ENTRIES);
104+
105+
for (int i = 0; i < count; i++) {
106+
final String result = searchResult.get(i);
107+
Label entryLabel = new Label(result);
108+
entryLabel.setPrefWidth(getWidth());
109+
110+
CustomMenuItem item = new CustomMenuItem(entryLabel, true);
111+
item.setHideOnClick(false);
112+
113+
menuItems.add(item);
114+
115+
item.setOnAction(actionEvent -> {
116+
setText(result);
117+
positionCaret(result.length());
118+
suggestionsList.hide();
119+
});
120+
}
121+
122+
suggestionsList.getItems().clear();
123+
suggestionsList.getItems().add(createPatternsSubMenu());
124+
suggestionsList.getItems().addAll(menuItems);
125+
126+
if (!menuItems.isEmpty()) {
127+
menuItems.getFirst().getContent().requestFocus();
128+
}
129+
}
130+
131+
private Menu createPatternsSubMenu() {
132+
Menu patternsSubMenu = new Menu(Localization.lang("All patterns"));
133+
134+
Map<CitationKeyPattern.Category, List<CitationKeyPattern>> categorizedPatterns =
135+
CitationKeyPattern.getAllPatterns().stream()
136+
.collect(Collectors.groupingBy(CitationKeyPattern::getCategory));
137+
138+
Map<CitationKeyPattern.Category, String> categoryNames = Map.of(
139+
CitationKeyPattern.Category.AUTHOR_RELATED, Localization.lang("Author related"),
140+
CitationKeyPattern.Category.EDITOR_RELATED, Localization.lang("Editor related"),
141+
CitationKeyPattern.Category.TITLE_RELATED, Localization.lang("Title related"),
142+
CitationKeyPattern.Category.OTHER_FIELDS, Localization.lang("Other fields"),
143+
CitationKeyPattern.Category.BIBENTRY_FIELDS, Localization.lang("BibEntry fields")
144+
);
145+
146+
for (Map.Entry<CitationKeyPattern.Category, String> entry : categoryNames.entrySet()) {
147+
CitationKeyPattern.Category category = entry.getKey();
148+
String categoryName = entry.getValue();
149+
150+
Menu categoryMenu = new Menu(categoryName);
151+
List<CitationKeyPattern> patterns = categorizedPatterns.getOrDefault(category, List.of());
152+
153+
for (CitationKeyPattern pattern : patterns) {
154+
MenuItem menuItem = new MenuItem(pattern.stringRepresentation());
155+
menuItem.setOnAction(event -> {
156+
setText(pattern.stringRepresentation());
157+
positionCaret(pattern.stringRepresentation().length());
158+
suggestionsList.hide();
159+
});
160+
categoryMenu.getItems().add(menuItem);
161+
}
162+
patternsSubMenu.getItems().add(categoryMenu);
163+
}
164+
return patternsSubMenu;
165+
}
166+
}
167+
}

src/main/java/org/jabref/gui/commonfxcontrols/CitationKeyPatternsPanel.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44

55
import javafx.beans.property.ListProperty;
66
import javafx.beans.property.ObjectProperty;
7+
import javafx.collections.FXCollections;
8+
import javafx.collections.ObservableList;
79
import javafx.fxml.FXML;
810
import javafx.scene.control.TableColumn;
911
import javafx.scene.control.TableRow;
1012
import javafx.scene.control.TableView;
11-
import javafx.scene.control.cell.TextFieldTableCell;
1213
import javafx.scene.input.KeyEvent;
1314

1415
import org.jabref.gui.icon.IconTheme;
1516
import org.jabref.gui.util.ValueTableCellFactory;
1617
import org.jabref.logic.citationkeypattern.AbstractCitationKeyPatterns;
18+
import org.jabref.logic.citationkeypattern.CitationKeyPattern;
1719
import org.jabref.logic.l10n.Localization;
1820
import org.jabref.logic.preferences.CliPreferences;
1921
import org.jabref.model.entry.BibEntryType;
@@ -34,9 +36,15 @@ public class CitationKeyPatternsPanel extends TableView<CitationKeyPatternsPanel
3436

3537
private long lastKeyPressTime;
3638
private String tableSearchTerm;
39+
private final ObservableList<String> patterns;
3740

3841
public CitationKeyPatternsPanel() {
3942
super();
43+
this.patterns = FXCollections.observableArrayList(
44+
CitationKeyPattern.getAllPatterns().stream()
45+
.map(CitationKeyPattern::stringRepresentation)
46+
.toList()
47+
);
4048

4149
ViewLoader.view(this)
4250
.root(this)
@@ -61,7 +69,7 @@ private void initialize() {
6169
patternColumn.setSortable(true);
6270
patternColumn.setReorderable(false);
6371
patternColumn.setCellValueFactory(cellData -> cellData.getValue().pattern());
64-
patternColumn.setCellFactory(TextFieldTableCell.forTableColumn());
72+
patternColumn.setCellFactory(_ -> new CitationKeyPatternSuggestionCell(patterns));
6573
patternColumn.setEditable(true);
6674
patternColumn.setOnEditCommit(
6775
(TableColumn.CellEditEvent<CitationKeyPatternsPanelItemModel, String> event) ->

src/main/java/org/jabref/gui/consistency/ConsistencyCheckDialog.java

+42-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package org.jabref.gui.consistency;
22

3+
import java.util.Arrays;
4+
import java.util.EnumSet;
35
import java.util.List;
6+
import java.util.Optional;
7+
import java.util.Set;
8+
import java.util.stream.Collectors;
49

510
import javafx.beans.property.ReadOnlyStringWrapper;
611
import javafx.collections.ListChangeListener;
@@ -20,6 +25,7 @@
2025
import org.jabref.logic.quality.consistency.BibliographyConsistencyCheck;
2126
import org.jabref.logic.quality.consistency.ConsistencyMessage;
2227
import org.jabref.model.entry.BibEntryTypesManager;
28+
import org.jabref.model.entry.field.SpecialField;
2329

2430
import com.airhacks.afterburner.views.ViewLoader;
2531
import com.tobiasdiez.easybind.EasyBind;
@@ -99,7 +105,7 @@ public void initialize() {
99105
return new ReadOnlyStringWrapper(message.get(columnIndex));
100106
});
101107

102-
tableColumn.setCellFactory(column -> new TableCell<ConsistencyMessage, String>() {
108+
tableColumn.setCellFactory(_ -> new TableCell<>() {
103109
@Override
104110
protected void updateItem(String item, boolean empty) {
105111
super.updateItem(item, empty);
@@ -112,17 +118,46 @@ protected void updateItem(String item, boolean empty) {
112118

113119
ConsistencySymbol.fromText(item)
114120
.ifPresentOrElse(
115-
symbol -> setGraphic(symbol.getIcon().getGraphicNode()),
116-
() -> {
117-
setGraphic(null);
118-
setText(item);
119-
}
120-
);
121+
symbol -> setGraphic(symbol.getIcon().getGraphicNode()),
122+
() -> {
123+
setGraphic(null);
124+
setText(item);
125+
}
126+
);
121127
}
122128
});
123129

124130
tableView.getColumns().add(tableColumn);
125131
}
132+
133+
EnumSet<ConsistencySymbol> targetSymbols = EnumSet.of(
134+
ConsistencySymbol.OPTIONAL_FIELD_AT_ENTRY_TYPE_CELL_ENTRY,
135+
ConsistencySymbol.REQUIRED_FIELD_AT_ENTRY_TYPE_CELL_ENTRY
136+
);
137+
138+
targetSymbols.stream()
139+
.map(ConsistencySymbol::getText)
140+
.forEach(this::removeColumnWithUniformValue);
141+
142+
Arrays.stream(SpecialField.values())
143+
.map(SpecialField::getDisplayName)
144+
.forEach(this::removeColumnByTitle);
145+
}
146+
147+
private void removeColumnWithUniformValue(String symbol) {
148+
List<TableColumn<ConsistencyMessage, ?>> columnToRemove = tableView.getColumns().stream()
149+
.filter(column -> {
150+
Set<String> values = tableView.getItems().stream()
151+
.map(item -> Optional.ofNullable(column.getCellObservableValue(item).getValue()).map(Object::toString).orElse(""))
152+
.collect(Collectors.toSet());
153+
return values.size() == 1 && values.contains(symbol);
154+
})
155+
.toList();
156+
tableView.getColumns().removeAll(columnToRemove);
157+
}
158+
159+
public void removeColumnByTitle(String columnName) {
160+
tableView.getColumns().removeIf(column -> column.getText().equalsIgnoreCase(columnName));
126161
}
127162

128163
@FXML

0 commit comments

Comments
 (0)