Skip to content

Commit 181e0b5

Browse files
committed
feat: allow shortscience and googlescholar search to be configurable
1 parent c70ac17 commit 181e0b5

File tree

11 files changed

+211
-60
lines changed

11 files changed

+211
-60
lines changed

jabgui/src/main/java/org/jabref/gui/maintable/MainTable.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ public MainTable(MainTableDataModel model,
163163
taskExecutor,
164164
Injector.instantiateModelOrService(JournalAbbreviationRepository.class),
165165
entryTypesManager,
166-
importHandler))
166+
importHandler,
167+
preferences.getImporterPreferences()))
167168
.withPseudoClass(MATCHING_SEARCH_AND_GROUPS, entry -> entry.matchCategory().isEqualTo(MatchCategory.MATCHING_SEARCH_AND_GROUPS))
168169
.withPseudoClass(MATCHING_SEARCH_NOT_GROUPS, entry -> entry.matchCategory().isEqualTo(MatchCategory.MATCHING_SEARCH_NOT_GROUPS))
169170
.withPseudoClass(MATCHING_GROUPS_NOT_SEARCH, entry -> entry.matchCategory().isEqualTo(MatchCategory.MATCHING_GROUPS_NOT_SEARCH))
@@ -180,10 +181,10 @@ public MainTable(MainTableDataModel model,
180181
// force match category column to be the first sort order, (match_category column is always the first column)
181182
this.getSortOrder().addFirst(getColumns().getFirst());
182183
this.getSortOrder().addListener((ListChangeListener<TableColumn<BibEntryTableViewModel, ?>>) change -> {
183-
if (!this.getSortOrder().getFirst().equals(getColumns().getFirst())) {
184-
this.getSortOrder().addFirst(getColumns().getFirst());
185-
}
186-
});
184+
if (!this.getSortOrder().getFirst().equals(getColumns().getFirst())) {
185+
this.getSortOrder().addFirst(getColumns().getFirst());
186+
}
187+
});
187188

188189
mainTablePreferences.getColumnPreferences().getColumnSortOrder().forEach(columnModel ->
189190
this.getColumns().stream()
@@ -414,7 +415,7 @@ private void setupKeyBindings(KeyBindingRepository keyBindings) {
414415
event.consume();
415416
break;
416417
case SCROLL_TO_PREVIOUS_MATCH_CATEGORY:
417-
scrollToPreviousMatchCategory();
418+
scrollToPreviousMatchCategory();
418419
event.consume();
419420
break;
420421
case OPEN_URL_OR_DOI:
@@ -576,13 +577,13 @@ public void setCitationMergeMode(boolean citationMerge) {
576577
}
577578

578579
private void updatePlaceholder(VBox placeholderBox) {
579-
if (database.getDatabase().getEntries().isEmpty()) {
580-
this.setPlaceholder(placeholderBox);
581-
// [impl->req~maintable.focus~1]
582-
requestFocus();
583-
} else {
584-
this.setPlaceholder(null);
585-
}
580+
if (database.getDatabase().getEntries().isEmpty()) {
581+
this.setPlaceholder(placeholderBox);
582+
// [impl->req~maintable.focus~1]
583+
requestFocus();
584+
} else {
585+
this.setPlaceholder(null);
586+
}
586587
}
587588

588589
private BibEntry addExampleEntry() {

jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory;
3737
import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
3838
import org.jabref.logic.citationstyle.CitationStylePreviewLayout;
39+
import org.jabref.logic.importer.ImporterPreferences;
3940
import org.jabref.logic.importer.WebFetchers;
4041
import org.jabref.logic.journals.JournalAbbreviationRepository;
4142
import org.jabref.logic.l10n.Localization;
@@ -62,7 +63,8 @@ public static ContextMenu create(BibEntryTableViewModel entry,
6263
TaskExecutor taskExecutor,
6364
JournalAbbreviationRepository abbreviationRepository,
6465
BibEntryTypesManager entryTypesManager,
65-
ImportHandler importHandler) {
66+
ImportHandler importHandler,
67+
ImporterPreferences importerPreferences) {
6668
ActionFactory factory = new ActionFactory();
6769
ContextMenu contextMenu = new ContextMenu();
6870

@@ -101,7 +103,7 @@ public static ContextMenu create(BibEntryTableViewModel entry,
101103
extractFileReferencesOffline,
102104

103105
factory.createMenuItem(StandardActions.OPEN_URL, new OpenUrlAction(dialogService, stateManager, preferences)),
104-
createSearchSubMenu(factory, dialogService, stateManager, preferences),
106+
createSearchSubMenu(factory, dialogService, stateManager, preferences, importerPreferences),
105107
new SeparatorMenuItem(),
106108

107109
new ChangeEntryTypeMenu(libraryTab.getSelectedEntries(), libraryTab.getBibDatabaseContext(), undoManager, entryTypesManager).asSubMenu(),
@@ -222,11 +224,12 @@ private static Menu createSendSubMenu(ActionFactory factory,
222224
private static Menu createSearchSubMenu(ActionFactory factory,
223225
DialogService dialogService,
224226
StateManager stateManager,
225-
GuiPreferences preferences) {
227+
GuiPreferences preferences,
228+
ImporterPreferences importerPreferences) {
226229
Menu searchMenu = factory.createMenu(StandardActions.SEARCH);
227230
searchMenu.getItems().addAll(
228-
factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager, preferences)),
229-
factory.createMenuItem(StandardActions.SEARCH_GOOGLE_SCHOLAR, new SearchGoogleScholarAction(dialogService, stateManager, preferences))
231+
factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager, preferences, importerPreferences)),
232+
factory.createMenuItem(StandardActions.SEARCH_GOOGLE_SCHOLAR, new SearchGoogleScholarAction(dialogService, stateManager, preferences, importerPreferences))
230233
);
231234
return searchMenu;
232235
}

jabgui/src/main/java/org/jabref/gui/maintable/SearchGoogleScholarAction.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.jabref.gui.actions.SimpleCommand;
1111
import org.jabref.gui.desktop.os.NativeDesktop;
1212
import org.jabref.gui.preferences.GuiPreferences;
13+
import org.jabref.logic.importer.ImporterPreferences;
1314
import org.jabref.logic.l10n.Localization;
1415
import org.jabref.logic.util.ExternalLinkCreator;
1516
import org.jabref.model.entry.BibEntry;
@@ -21,11 +22,13 @@ public class SearchGoogleScholarAction extends SimpleCommand {
2122
private final DialogService dialogService;
2223
private final StateManager stateManager;
2324
private final GuiPreferences preferences;
25+
private final ExternalLinkCreator externalLinkCreator;
2426

25-
public SearchGoogleScholarAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences) {
27+
public SearchGoogleScholarAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences, ImporterPreferences importerPreferences) {
2628
this.dialogService = dialogService;
2729
this.stateManager = stateManager;
2830
this.preferences = preferences;
31+
this.externalLinkCreator = new ExternalLinkCreator(importerPreferences);
2932

3033
BooleanExpression fieldIsSet = isFieldSetForSelectedEntry(StandardField.TITLE, stateManager);
3134
this.executable.bind(needsEntriesSelected(1, stateManager).and(fieldIsSet));
@@ -40,7 +43,7 @@ public void execute() {
4043
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
4144
return;
4245
}
43-
ExternalLinkCreator.getGoogleScholarSearchURL(bibEntries.getFirst()).ifPresent(url -> {
46+
externalLinkCreator.getGoogleScholarSearchURL(bibEntries.getFirst()).ifPresent(url -> {
4447
try {
4548
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
4649
} catch (IOException ex) {

jabgui/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.jabref.gui.actions.SimpleCommand;
1111
import org.jabref.gui.desktop.os.NativeDesktop;
1212
import org.jabref.gui.preferences.GuiPreferences;
13+
import org.jabref.logic.importer.ImporterPreferences;
1314
import org.jabref.logic.l10n.Localization;
1415
import org.jabref.logic.util.ExternalLinkCreator;
1516
import org.jabref.model.entry.BibEntry;
@@ -22,11 +23,13 @@ public class SearchShortScienceAction extends SimpleCommand {
2223
private final DialogService dialogService;
2324
private final StateManager stateManager;
2425
private final GuiPreferences preferences;
26+
private final ExternalLinkCreator externalLinkCreator;
2527

26-
public SearchShortScienceAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences) {
28+
public SearchShortScienceAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences, ImporterPreferences importerPreferences) {
2729
this.dialogService = dialogService;
2830
this.stateManager = stateManager;
2931
this.preferences = preferences;
32+
this.externalLinkCreator = new ExternalLinkCreator(importerPreferences);
3033

3134
BooleanExpression fieldIsSet = isFieldSetForSelectedEntry(StandardField.TITLE, stateManager);
3235
this.executable.bind(needsEntriesSelected(1, stateManager).and(fieldIsSet));
@@ -41,7 +44,7 @@ public void execute() {
4144
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
4245
return;
4346
}
44-
ExternalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
47+
externalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
4548
try {
4649
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
4750
} catch (IOException ex) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.jabref.gui.preferences.websearch;
2+
3+
import javafx.beans.property.SimpleStringProperty;
4+
import javafx.beans.property.StringProperty;
5+
6+
public class SearchEngineItem {
7+
private final StringProperty name;
8+
private final StringProperty urlTemplate;
9+
10+
public SearchEngineItem(String name, String urlTemplate) {
11+
this.name = new SimpleStringProperty(name);
12+
this.urlTemplate = new SimpleStringProperty(urlTemplate);
13+
}
14+
15+
public StringProperty nameProperty() {
16+
return name;
17+
}
18+
19+
public StringProperty urlTemplateProperty() {
20+
return urlTemplate;
21+
}
22+
23+
public String getName() {
24+
return name.get();
25+
}
26+
27+
public String getUrlTemplate() {
28+
return urlTemplate.get();
29+
}
30+
31+
public void setUrlTemplate(String urlTemplate) {
32+
this.urlTemplate.set(urlTemplate);
33+
}
34+
}

jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTab.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public class WebSearchTab extends AbstractPreferenceTabView<WebSearchTabViewMode
4141
@FXML private CheckBox grobidEnabled;
4242
@FXML private TextField grobidURL;
4343

44+
@FXML private TableView<SearchEngineItem> searchEngineTable;
45+
@FXML private TableColumn<SearchEngineItem, String> searchEngineName;
46+
@FXML private TableColumn<SearchEngineItem, String> searchEngineUrlTemplate;
47+
4448
@FXML private TableView<FetcherApiKey> apiKeySelectorTable;
4549
@FXML private TableColumn<FetcherApiKey, String> apiKeyName;
4650
@FXML private TableColumn<FetcherApiKey, String> customApiKey;
@@ -71,6 +75,16 @@ public String getTabName() {
7175
public void initialize() {
7276
this.viewModel = new WebSearchTabViewModel(preferences, dialogService, refAiEnabled);
7377

78+
searchEngineName.setCellValueFactory(param -> param.getValue().nameProperty());
79+
searchEngineName.setCellFactory(TextFieldTableCell.forTableColumn());
80+
searchEngineName.setEditable(false);
81+
82+
searchEngineUrlTemplate.setCellValueFactory(param -> param.getValue().urlTemplateProperty());
83+
searchEngineUrlTemplate.setCellFactory(TextFieldTableCell.forTableColumn());
84+
searchEngineUrlTemplate.setEditable(true);
85+
86+
searchEngineTable.setItems(viewModel.getSearchEngines());
87+
7488
enableWebSearch.selectedProperty().bindBidirectional(viewModel.enableWebSearchProperty());
7589
warnAboutDuplicatesOnImport.selectedProperty().bindBidirectional(viewModel.warnAboutDuplicatesOnImportProperty());
7690
downloadLinkedOnlineFiles.selectedProperty().bindBidirectional(viewModel.shouldDownloadLinkedOnlineFiles());

jabgui/src/main/java/org/jabref/gui/preferences/websearch/WebSearchTabViewModel.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.IOException;
44
import java.net.HttpURLConnection;
5+
import java.util.Map;
56
import java.util.Optional;
67
import java.util.stream.Collectors;
78

@@ -60,6 +61,8 @@ public class WebSearchTabViewModel implements PreferenceTabViewModel {
6061
private final BooleanProperty apikeyPersistProperty = new SimpleBooleanProperty();
6162
private final BooleanProperty apikeyPersistAvailableProperty = new SimpleBooleanProperty();
6263

64+
private final ObservableList<SearchEngineItem> searchEngines = FXCollections.observableArrayList();
65+
6366
private final DialogService dialogService;
6467
private final CliPreferences preferences;
6568
private final DOIPreferences doiPreferences;
@@ -82,6 +85,7 @@ public WebSearchTabViewModel(CliPreferences preferences, DialogService dialogSer
8285
this.refAiEnabled = refAiEnabled;
8386

8487
setupPlainCitationParsers(preferences);
88+
setupSearchEngines();
8589
}
8690

8791
private void setupPlainCitationParsers(CliPreferences preferences) {
@@ -122,6 +126,14 @@ private void setupPlainCitationParsers(CliPreferences preferences) {
122126
});
123127
}
124128

129+
private void setupSearchEngines() {
130+
// Add default search engines
131+
searchEngines.addAll(
132+
new SearchEngineItem("Google Scholar", "https://scholar.google.com/scholar?q={title}"),
133+
new SearchEngineItem("Short Science", "https://www.shortscience.org/internalsearch?q={title}")
134+
);
135+
}
136+
125137
@Override
126138
public void setValues() {
127139
enableWebSearchProperty.setValue(importerPreferences.areImporterEnabled());
@@ -151,6 +163,13 @@ public void setValues() {
151163
return new StudyCatalogItem(name, enabled);
152164
})
153165
.toList());
166+
167+
// Load custom URL templates from preferences if they exist
168+
Map<String, String> savedTemplates = preferences.getImporterPreferences().getSearchEngineUrlTemplates();
169+
if (!savedTemplates.isEmpty()) {
170+
searchEngines.clear();
171+
savedTemplates.forEach((name, url) -> searchEngines.add(new SearchEngineItem(name, url)));
172+
}
154173
}
155174

156175
@Override
@@ -175,6 +194,14 @@ public void storeSettings() {
175194
if (apikeyPersistAvailableProperty.get()) {
176195
preferences.getImporterPreferences().getApiKeys().addAll(apiKeys);
177196
}
197+
198+
// Save custom URL templates to preferences
199+
Map<String, String> templates = searchEngines.stream()
200+
.collect(Collectors.toMap(
201+
SearchEngineItem::getName,
202+
SearchEngineItem::getUrlTemplate
203+
));
204+
preferences.getImporterPreferences().setSearchEngineUrlTemplates(templates);
178205
}
179206

180207
public BooleanProperty enableWebSearchProperty() {
@@ -237,6 +264,10 @@ public BooleanProperty getApikeyPersistProperty() {
237264
return apikeyPersistProperty;
238265
}
239266

267+
public ObservableList<SearchEngineItem> getSearchEngines() {
268+
return searchEngines;
269+
}
270+
240271
public void checkCustomApiKey() {
241272
final String apiKeyName = selectedApiKeyProperty.get().getName();
242273

jabgui/src/main/resources/org/jabref/gui/preferences/websearch/WebSearchTab.fxml

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<?import javafx.scene.layout.HBox?>
1212
<?import javafx.scene.layout.VBox?>
1313
<?import javafx.scene.control.ComboBox?>
14+
<?import org.jabref.gui.preferences.websearch.SearchEngineItem?>
1415
<fx:root spacing="10.0" type="VBox"
1516
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
1617
fx:controller="org.jabref.gui.preferences.websearch.WebSearchTab">
@@ -38,9 +39,9 @@
3839
editable="true">
3940
<columns>
4041
<TableColumn minWidth="90" prefWidth="70"
41-
fx:id="catalogEnabledColumn"
42-
text="%Enabled"
43-
/>
42+
fx:id="catalogEnabledColumn"
43+
text="%Enabled"
44+
/>
4445
<TableColumn
4546
fx:id="catalogColumn"
4647
text="%Catalog"/>
@@ -58,6 +59,28 @@
5859
<TextField fx:id="grobidURL" HBox.hgrow="ALWAYS"/>
5960
</HBox>
6061

62+
<Label styleClass="sectionHeader" text="%Search Engine URL Templates"/>
63+
<Label text="%( Note: Press return to commit changes in the table! )"/>
64+
<TableView
65+
fx:id="searchEngineTable"
66+
VBox.vgrow="ALWAYS"
67+
editable="true">
68+
<columns>
69+
<TableColumn minWidth="120"
70+
fx:id="searchEngineName"
71+
text="%Search Engine"
72+
editable="false"/>
73+
<TableColumn minWidth="300"
74+
fx:id="searchEngineUrlTemplate"
75+
text="%URL Template"
76+
editable="true"/>
77+
</columns>
78+
<columnResizePolicy>
79+
<TableView
80+
fx:constant="CONSTRAINED_RESIZE_POLICY"/>
81+
</columnResizePolicy>
82+
</TableView>
83+
6184
<Label styleClass="sectionHeader" text="%Custom API key"/>
6285
<Label text="%( Note: Press return to commit changes in the table! )"/>
6386
<TableView
@@ -70,12 +93,12 @@
7093
text="%Catalog"
7194
/>
7295
<TableColumn minWidth="120"
73-
fx:id="customApiKey"
74-
text="Key"/>
96+
fx:id="customApiKey"
97+
text="Key"/>
7598

7699
<TableColumn minWidth="120" prefWidth="120"
77-
fx:id="useCustomApiKey"
78-
text="Use Custom Key"/>
100+
fx:id="useCustomApiKey"
101+
text="Use Custom Key"/>
79102
</columns>
80103
<columnResizePolicy>
81104
<TableView

0 commit comments

Comments
 (0)