Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
99c3cf7
Prevent bulk imports from adding entries to navigation history
shubhamk0205 Dec 7, 2025
6a390a5
Updated ImportHandlerTest
shubhamk0205 Dec 8, 2025
5787d36
fixed CI errors
shubhamk0205 Dec 8, 2025
408a46b
fixed CI errors
shubhamk0205 Dec 8, 2025
dd7811c
fixed CI errors
shubhamk0205 Dec 8, 2025
dc62aa1
fixed CI errors
shubhamk0205 Dec 8, 2025
0119f15
fixed CI errors
shubhamk0205 Dec 8, 2025
addcd66
fixed CI errors
shubhamk0205 Dec 8, 2025
8d8ab3d
fixed CI errors
shubhamk0205 Dec 8, 2025
5772178
Merge branch 'main' into fix-for-issue-13878
shubhamk0205 Dec 8, 2025
789b294
Fix link
koppor Dec 8, 2025
4b8ed07
Proxy Preference reset (#14554)
lalit2506verma Dec 8, 2025
37e5caf
Fix typos (#14552)
koppor Dec 8, 2025
d24aa92
Add LlmPlainCitationParserTest (#14553)
koppor Dec 8, 2025
dd95e05
Create README.md for JabKit project
koppor Dec 9, 2025
d101b2e
Add JDK24 for gradle (#14560)
koppor Dec 9, 2025
d5c3200
Add fallback (#14520)
koppor Dec 9, 2025
8286ff2
Rerun only on main repo
koppor Dec 10, 2025
8de881f
Add documentation link for jabsrv
koppor Dec 10, 2025
d228c46
Implement reset for MainTablePreferences (#14523)
nbelwang Dec 10, 2025
315fb42
change: Rename web labels 'Search pre-configured' to 'Search pre-sele…
mayank1008-tech Dec 11, 2025
7e1ddc6
Refactor PagesChecker: Extract duplicate regex constants (#14567)
Zeglow Dec 11, 2025
b6ab730
Refactor some formats tests to use parameterized tests (#14558)
merlinymy Dec 11, 2025
a23553c
New Crowdin updates (#14570)
Siedlerchr Dec 11, 2025
aafb809
Add 15 non-standard BibLaTeX entry types with .withType() method (#14…
shubhamk0205 Dec 11, 2025
9efbe2d
Fix link
koppor Dec 8, 2025
225fbb5
Proxy Preference reset (#14554)
lalit2506verma Dec 8, 2025
6825062
Fix typos (#14552)
koppor Dec 8, 2025
851a2e8
Add LlmPlainCitationParserTest (#14553)
koppor Dec 8, 2025
76351ea
Create README.md for JabKit project
koppor Dec 9, 2025
fde4401
Add JDK24 for gradle (#14560)
koppor Dec 9, 2025
f4cb182
Add fallback (#14520)
koppor Dec 9, 2025
db6a8fa
Rerun only on main repo
koppor Dec 10, 2025
8cae2f3
Add documentation link for jabsrv
koppor Dec 10, 2025
a82b2b0
Implement reset for MainTablePreferences (#14523)
nbelwang Dec 10, 2025
92d53ca
change: Rename web labels 'Search pre-configured' to 'Search pre-sele…
mayank1008-tech Dec 11, 2025
29e3986
Refactor PagesChecker: Extract duplicate regex constants (#14567)
Zeglow Dec 11, 2025
36b881c
Refactor some formats tests to use parameterized tests (#14558)
merlinymy Dec 11, 2025
dc89fc6
New Crowdin updates (#14570)
Siedlerchr Dec 11, 2025
d7451cf
Add 15 non-standard BibLaTeX entry types with .withType() method (#14…
shubhamk0205 Dec 11, 2025
cde171f
Fix bulk imports adding entries to navigation history
shubhamk0205 Dec 11, 2025
73119df
Prevent bulk imports from adding entries to navigation history
shubhamk0205 Dec 7, 2025
7857fc2
Updated ImportHandlerTest
shubhamk0205 Dec 8, 2025
c80dd53
fixed CI errors
shubhamk0205 Dec 8, 2025
9dedc3d
fixed CI errors
shubhamk0205 Dec 8, 2025
131221c
fixed CI errors
shubhamk0205 Dec 8, 2025
8d367a2
fixed CI errors
shubhamk0205 Dec 8, 2025
60d89e4
fixed CI errors
shubhamk0205 Dec 8, 2025
c305e47
fixed CI errors
shubhamk0205 Dec 8, 2025
e2bfbba
fixed CI errors
shubhamk0205 Dec 8, 2025
1e1f734
Fix bulk imports adding entries to navigation history
shubhamk0205 Dec 11, 2025
daa6d59
Merge branch 'fix-for-issue-13878' of https://github.com/shubhamk0205…
shubhamk0205 Dec 11, 2025
26461d4
Revert accidental merge daa6d595ad that pulled main into branch
shubhamk0205 Dec 11, 2025
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
47 changes: 38 additions & 9 deletions jabgui/src/main/java/org/jabref/gui/LibraryTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public class LibraryTab extends Tab implements CommandSelectionTab {
private final BooleanProperty canGoBackProperty = new SimpleBooleanProperty(false);
private final BooleanProperty canGoForwardProperty = new SimpleBooleanProperty(false);
private boolean backOrForwardNavigationActionTriggered = false;
private boolean bulkImportInProgress = false;

private BibDatabaseContext bibDatabaseContext;

Expand Down Expand Up @@ -239,7 +240,9 @@ private void initializeComponentsAndListeners(boolean isDummyContext) {
undoManager,
stateManager,
dialogService,
taskExecutor);
taskExecutor,
this::startBulkImport,
this::endBulkImport);

setupMainPanel();
setupAutoCompletion();
Expand Down Expand Up @@ -825,14 +828,19 @@ public void insertEntries(final List<BibEntry> entries) {
return;
}

importHandler.importCleanedEntries(null, entries);
getUndoManager().addEdit(new UndoableInsertEntries(bibDatabaseContext.getDatabase(), entries));
markBaseChanged();
stateManager.setSelectedEntries(entries);
if (preferences.getEntryEditorPreferences().shouldOpenOnNewEntry()) {
showAndEdit(entries.getFirst());
} else {
clearAndSelect(entries.getFirst());
startBulkImport();
try {
importHandler.importCleanedEntries(null, entries);
getUndoManager().addEdit(new UndoableInsertEntries(bibDatabaseContext.getDatabase(), entries));
markBaseChanged();
stateManager.setSelectedEntries(entries);
if (preferences.getEntryEditorPreferences().shouldOpenOnNewEntry()) {
showAndEdit(entries.getFirst());
} else {
clearAndSelect(entries.getFirst());
}
} finally {
endBulkImport();
}
}

Expand Down Expand Up @@ -1025,6 +1033,11 @@ private void newEntryShowing(BibEntry entry) {
return;
}

// skip history updates if this is from a bulk import operation
if (bulkImportInProgress) {
return;
}

navigationHistory.add(entry);
updateNavigationState();
}
Expand All @@ -1038,6 +1051,22 @@ public void updateNavigationState() {
canGoForwardProperty.set(canGoForward());
}

/**
* Marks the start of a bulk import operation.
* During bulk import, selection changes will not be added to navigation history.
*/
public void startBulkImport() {
bulkImportInProgress = true;
}

/**
* Marks the end of a bulk import operation.
* Normal navigation history tracking resumes after this call.
*/
public void endBulkImport() {
bulkImportInProgress = false;
}

Comment on lines 1062 to 1082
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hacky as hell

/**
* Creates a new library tab. Contents are loaded by the {@code dataLoadingTask}. Most of the other parameters are required by {@code resetChangeMonitor()}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ public void importEntries(List<CitationRelationItem> entriesToImport, CitationFe
undoManager,
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);
CitationKeyGenerator generator = new CitationKeyGenerator(databaseContext, preferences.getCitationKeyPatternPreferences());
boolean generateNewKeyOnImport = preferences.getImporterPreferences().generateNewKeyOnImportProperty().get();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,28 @@ public class ImportHandler {
private final DialogService dialogService;
private final TaskExecutor taskExecutor;
private final FilePreferences filePreferences;
@Nullable
private final Runnable onBulkImportStart;
@Nullable
private final Runnable onBulkImportEnd;

public ImportHandler(BibDatabaseContext targetBibDatabaseContext,
GuiPreferences preferences,
FileUpdateMonitor fileupdateMonitor,
UndoManager undoManager,
StateManager stateManager,
DialogService dialogService,
TaskExecutor taskExecutor) {
TaskExecutor taskExecutor,
@Nullable Runnable onBulkImportStart,
@Nullable Runnable onBulkImportEnd) {
this.targetBibDatabaseContext = targetBibDatabaseContext;
this.preferences = preferences;
this.fileUpdateMonitor = fileupdateMonitor;
this.stateManager = stateManager;
this.dialogService = dialogService;
this.taskExecutor = taskExecutor;
this.onBulkImportStart = onBulkImportStart;
this.onBulkImportEnd = onBulkImportEnd;

this.filePreferences = preferences.getFilePreferences();

Expand Down Expand Up @@ -236,21 +244,31 @@ public void importEntries(List<BibEntry> entries) {
}

public void importCleanedEntries(@Nullable TransferInformation transferInformation, List<BibEntry> entries) {
targetBibDatabaseContext.getDatabase().insertEntries(entries);
generateKeys(entries);
setAutomaticFields(entries);
addToGroups(entries, stateManager.getSelectedGroups(targetBibDatabaseContext));
addToImportEntriesGroup(entries);

if (transferInformation != null) {
entries.stream().forEach(entry -> {
LinkedFileTransferHelper
.adjustLinkedFilesForTarget(filePreferences, transferInformation, targetBibDatabaseContext, entry);
});
boolean isBulkImport = entries.size() > 1;
if (isBulkImport && onBulkImportStart != null) {
onBulkImportStart.run();
}
try {
targetBibDatabaseContext.getDatabase().insertEntries(entries);
generateKeys(entries);
setAutomaticFields(entries);
addToGroups(entries, stateManager.getSelectedGroups(targetBibDatabaseContext));
addToImportEntriesGroup(entries);

if (transferInformation != null) {
entries.stream().forEach(entry -> {
LinkedFileTransferHelper
.adjustLinkedFilesForTarget(filePreferences, transferInformation, targetBibDatabaseContext, entry);
});
}

// TODO: Should only be done if NOT copied from other library
entries.stream().forEach(entry -> downloadLinkedFiles(entry));
// TODO: Should only be done if NOT copied from other library
entries.stream().forEach(entry -> downloadLinkedFiles(entry));
} finally {
if (isBulkImport && onBulkImportEnd != null) {
onBulkImportEnd.run();
}
}
}

public void importEntryWithDuplicateCheck(@Nullable TransferInformation transferInformation, BibEntry entry) {
Expand Down Expand Up @@ -472,21 +490,31 @@ public void importEntriesWithDuplicateCheck(@Nullable TransferInformation transf
}

public void importEntriesWithDuplicateCheck(@Nullable TransferInformation transferInformation, List<BibEntry> entriesToAdd, EntryImportHandlerTracker tracker) {
boolean firstEntry = true;
for (BibEntry entry : entriesToAdd) {
if (firstEntry) {
LOGGER.debug("First entry to import, we use BREAK (\"Ask every time\") as decision");
importEntryWithDuplicateCheck(transferInformation, entry, BREAK, tracker);
firstEntry = false;
continue;
boolean isBulkImport = entriesToAdd.size() > 1;
if (isBulkImport && onBulkImportStart != null) {
onBulkImportStart.run();
}
try {
boolean firstEntry = true;
for (BibEntry entry : entriesToAdd) {
if (firstEntry) {
LOGGER.debug("First entry to import, we use BREAK (\"Ask every time\") as decision");
importEntryWithDuplicateCheck(transferInformation, entry, BREAK, tracker);
firstEntry = false;
continue;
}
if (preferences.getMergeDialogPreferences().shouldMergeApplyToAllEntries()) {
DuplicateResolverDialog.DuplicateResolverResult decision = preferences.getMergeDialogPreferences().getAllEntriesDuplicateResolverDecision();
LOGGER.debug("Not first entry, pref flag is true, we use {}", decision);
importEntryWithDuplicateCheck(transferInformation, entry, decision, tracker);
} else {
LOGGER.debug("not first entry, not pref flag, break will be used");
importEntryWithDuplicateCheck(transferInformation, entry, BREAK, tracker);
}
}
if (preferences.getMergeDialogPreferences().shouldMergeApplyToAllEntries()) {
DuplicateResolverDialog.DuplicateResolverResult decision = preferences.getMergeDialogPreferences().getAllEntriesDuplicateResolverDecision();
LOGGER.debug("Not first entry, pref flag is true, we use {}", decision);
importEntryWithDuplicateCheck(transferInformation, entry, decision, tracker);
} else {
LOGGER.debug("not first entry, not pref flag, break will be used");
importEntryWithDuplicateCheck(transferInformation, entry, BREAK, tracker);
} finally {
if (isBulkImport && onBulkImportEnd != null) {
onBulkImportEnd.run();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService,
undoManager,
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);

this.fileFilterList = FXCollections.observableArrayList(
new FileExtensionViewModel(StandardFileType.ANY_FILE, preferences.getExternalApplicationsPreferences()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,9 @@ private void handleOnDragDropped(TreeTableRow<GroupNodeViewModel> row, GroupNode
undoManager,
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);
List<Path> files = dragboard.getFiles().stream().map(File::toPath).collect(Collectors.toList());
stateManager.setSelectedGroups(database, List.of(row.getItem().getGroupNode()));
importHandler.importFilesInBackground(files, event.getTransferMode())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ public void importEntries(List<BibEntry> entriesToImport, boolean shouldDownload
undoManager,
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);
importHandler.importEntriesWithDuplicateCheck(null, entriesToImport);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,9 @@ public void executeLookupIdentifier(boolean searchComposite) {
libraryTab.getUndoManager(),
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);
handler.importEntryWithDuplicateCheck(new TransferInformation(libraryTab.getBibDatabaseContext(), TransferMode.NONE), result.get());

executedSuccessfully.set(true);
Expand Down Expand Up @@ -420,7 +422,9 @@ public void executeInterpretCitations() {
libraryTab.getUndoManager(),
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);
handler.importEntriesWithDuplicateCheck(null, result.get());

executedSuccessfully.set(true);
Expand Down Expand Up @@ -505,7 +509,9 @@ public void executeSpecifyBibtex() {
libraryTab.getUndoManager(),
stateManager,
dialogService,
taskExecutor);
taskExecutor,
null,
null);
handler.importEntriesWithDuplicateCheck(null, result.get());

executedSuccessfully.set(true);
Expand Down
Loading
Loading