Skip to content

Refined the "Select files to import" page in "Search for unlinked local files" dialog#15110

Merged
Siedlerchr merged 30 commits into
JabRef:mainfrom
ZiadAbdElFatah:fix-for-issue-13689
Mar 1, 2026
Merged

Refined the "Select files to import" page in "Search for unlinked local files" dialog#15110
Siedlerchr merged 30 commits into
JabRef:mainfrom
ZiadAbdElFatah:fix-for-issue-13689

Conversation

@ZiadAbdElFatah

@ZiadAbdElFatah ZiadAbdElFatah commented Feb 14, 2026

Copy link
Copy Markdown
Collaborator

Related issues and pull requests

Closes #13689

PR Description

Refined the "Select files to import" page in "Search for unlinked local files" dialog to give the users the choice of linking the file to a related entry or import it to a new entry.

Steps to test

First open test-support/src/manual-tests/issue-13689/issue-13689.bib library
image
Select Lookup - Search for unlinked local files
image
This dialog will pop up
image

Click Next
image

Now the dialog provides jumping to related entries for each unlinked file. If the file can be linked to one entry or more, the entries will be in a dropdown, if the file isn't linked to any entry, it will have a blank beside it.
On clicking Next, if there is a selected entry for a file, this file will be linked to the selected entry otherwise, it will be imported into a new entry.
image
image

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • I manually tested my changes in running JabRef (always required)
  • [/] I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

@github-actions github-actions Bot added the good second issue Issues that involve a tour of two or three interweaved components in JabRef label Feb 14, 2026

relatedEntries.setAll(relatedEntriesList);
return relatedEntries;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the part I was asking about in this comment #13689 (comment).
This is the function that gets the related entries that the files can be linked with. I compare them with the citation key, with the different patterns of the file name, I can't find a specific way to parse the citation key from the file name.
I read the documentation you provided here #13689 (comment), but I still don't have a solution @koppor.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't know what the existing code does. Need to read the existing code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I am continuing investigating the existing code also.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The UnlinkedFilesCrawler and the UnlinkedPDFFileFilter are your friends

/// Creates an instance by passing a {@link org.jabref.model.database.BibDatabase} which will be used for the searches.
public DatabaseFileLookup(@NonNull BibDatabaseContext databaseContext, FilePreferences filePreferences) {
possibleFilePaths = Optional.ofNullable(databaseContext.getFileDirectories(filePreferences))
.orElse(new ArrayList<>());
for (BibEntry entry : databaseContext.getDatabase().getEntries()) {
fileCache.addAll(parseFileField(entry));
}
this.pathOfDatabase = databaseContext.getDatabasePath().orElse(Path.of(""));
}

Roughly speaking, we create a list of all values of the file field in the library from all entries.
Then the crawlers use the Filter to check if the current found file a.pdf is already in the list of the entries, and so on

I think @koppor is confusing the logic here with "Automatically set File Links" which tries to automatically find matching files AutoLinkFilesAction.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is kind of inverse action - and should be properly designed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The UnlinkedFilesCrawler and the UnlinkedPDFFileFilter are your friends

Thanks a lot for the guidance.

I think @koppor is confusing the logic here with "Automatically set File Links" which tries to automatically find matching files AutoLinkFilesAction.

Yeah I am really confused, is the refined dialog about only letting the user know that there are entries that are related to the file and the file will always be in new entry if it is imported or the user can link the files related to some entries without creating a new entry for them.

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Feb 14, 2026
@testlens-app

This comment has been minimized.

List<BibEntry> relatedEntriesList = new ArrayList<>();
List<BibEntry> allEntries = bibDatabase.getDatabase().getEntries();

AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(

@ZiadAbdElFatah ZiadAbdElFatah Feb 16, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I found this method here

private List<LinkedFileViewModel> findAssociatedNotLinkedFiles(BibEntry entry) {
List<LinkedFileViewModel> result = new ArrayList<>();
AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(
databaseContext,
preferences.getExternalApplicationsPreferences(),
preferences.getFilePreferences(),
preferences.getAutoLinkPreferences());
try {
Collection<LinkedFile> linkedFiles = util.findAssociatedNotLinkedFiles(entry);
for (LinkedFile linkedFile : linkedFiles) {
LinkedFileViewModel newLinkedFile = new LinkedFileViewModel(
linkedFile,
entry,
databaseContext,
taskExecutor,
dialogService,
preferences);
newLinkedFile.markAsAutomaticallyFound();
result.add(newLinkedFile);
}
} catch (IOException e) {
dialogService.showErrorDialogAndWait("Error accessing the file system", e);
}
LOGGER.trace("Found {} associated files for entry {}", result.size(), entry.getCitationKey());
return result;
}

But I had to change the CliPreferences to GuiPreferences to be able to call getExternalApplicationsPreferences(). Is this ok?
This method gets the files that are found but not linked to each entry, so I think it's the best method for searching the related entries for each file.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this comment should have gone to line 79?

Since we are in the GUI, this change is OK.

@ZiadAbdElFatah

Copy link
Copy Markdown
Collaborator Author

Now, this PR, when the Find unlinked files is invoked by the user, it lets them know if there are existing entries that the files can be linked to already, instead of creating a new entry, but it doesn't handle file linking directly.
So, should we add the linking, or is the current implementation enough @koppor @Siedlerchr?
Here is how the dialog looks right now:

  1. Single related entry:
image image 2. Multiple related entries: image image 3. No related entries: image

koppor
koppor previously requested changes Feb 16, 2026

@koppor koppor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While reading #13689 (comment), I think, this PR goes in the right direction.


I wonder why so much is happing in the GUI. But this is PR #14710 - and neesd to be fixed seperately.

preferences.getFilePreferences(),
preferences.getAutoLinkPreferences());

for (BibEntry entry : allEntries) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The algorithm should be explained.

List<BibEntry> relatedEntriesList = new ArrayList<>();
List<BibEntry> allEntries = bibDatabase.getDatabase().getEntries();

AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this comment should have gone to line 79?

Since we are in the GUI, this change is OK.

Comment on lines +229 to +232
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When can entry or item be null? Is this a wrong call?

If you cannot avoid, please add a JavaDoc comment and also @Nulalble

Furthermore, return early. meaing: if, then return

and then no else branch.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

When can entry or item be null? Is this a wrong call?

I found this doc for the updateItem method
image
So I think it's safer to check if empty or item is null.

} else {
leftSide.getChildren().clear();
CheckBox checkBox = (CheckBox) getGraphic();
if (checkBox != null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When can checkbox be null?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I checked the normal scenarios where the checkbox can be null, and I think no one of them will happen in our case, so I think this check is dummy here.

cellContent.getChildren().clear();
cellContent.getChildren().add(leftSide);

if (item.getPath().toFile().isFile()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can it be no -file? when?

exit early again.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

can it be no -file? when?

exit early again.

We want to show blank space if it's a directory, not a file.

}
}
setGraphic(cellContent);
setText(null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why no next? Java comment

jumpToEntryButton.getStyleClass().add("icon-button");
jumpToEntryButton.setTooltip(new Tooltip(Localization.lang("Jump to entry")));
jumpToEntryButton.setOnAction(_ -> {
BibEntry selectedEntry = relatedEntries.getValue();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When can it happen that there is no value? --> add java ocmment

new ViewModelTreeCellFactory<FileNodeViewModel>()
.withText(FileNodeViewModel::getDisplayTextWithEditDate)
.install(unlinkedFilesList);
unlinkedFilesList.setCellFactory(_ -> new CheckBoxTreeCell<>() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Write a separate class - this is way too long for an anoymous class.

@github-actions

Copy link
Copy Markdown
Contributor

Your pull request conflicts with the target branch.

Please merge with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

@ZiadAbdElFatah

ZiadAbdElFatah commented Feb 17, 2026

Copy link
Copy Markdown
Collaborator Author

I wonder why so much is happing in the GUI.

It's just because it's my first time working with GUI using JavaFX in this way.

@testlens-app

This comment has been minimized.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

pass StateManager via constructor parameter


unlinkedFilesList = new CheckTreeView<>();
new ViewModelTreeCellFactory<FileNodeViewModel>().withText(FileNodeViewModel::getDisplayTextWithEditDate).install(unlinkedFilesList);
unlinkedFilesList.setCellFactory(_ -> new UnlinkedFilesCellFactory(this.viewModel.getStateManager(), viewModel));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pas stateManager from constructor down

@Siedlerchr Siedlerchr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

some UX improvements

@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Feb 28, 2026
@testlens-app

This comment has been minimized.

@ZiadAbdElFatah ZiadAbdElFatah changed the title Refined the "Search for unlinked local files" dialog Refined the "Select files to import" page in "Search for unlinked local files" dialog Feb 28, 2026
@testlens-app

testlens-app Bot commented Feb 28, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 76a24ba
▶️ Tests: 11252 executed
⚪️ Checks: 66/66 completed


Learn more about TestLens at testlens.app.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Feb 28, 2026

@Siedlerchr Siedlerchr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me now!

@Siedlerchr Siedlerchr added this pull request to the merge queue Mar 1, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label Mar 1, 2026
Merged via the queue into JabRef:main with commit 28759fe Mar 1, 2026
65 of 66 checks passed
@ZiadAbdElFatah ZiadAbdElFatah deleted the fix-for-issue-13689 branch March 1, 2026 22:36
priyanshu16095 pushed a commit to priyanshu16095/jabref that referenced this pull request Mar 3, 2026
…al files" dialog (JabRef#15110)

* Refined the Find unlinked files dialog

* Added search for entries by pdf names

* Added search icon for the selected entry

* Fix submodules

* Refactored the searching process

* Fix submodules

* Enhanced some code

* Added manual test

* Fix submodules

* Added CHANGELOG entry

* Fix submodules

* Fix submodules

* Fix submodules

* Fix submodules

* Changed "new" to localized one

* Fixed some typos

* Moved the CHANGELOG entry under Unreleased

* Changed the tree cell of the FileSelectionPage

* Passed StateManager via constructor parameter

* Added the dropdown for one entry

* Added do not link to deselect the entry in the dropdown

* Added linking the file to the selected entry from the dropdown

* Refined the Select files to import page in Search for unlinked local files dialog

* Removed unused expressions
Siedlerchr added a commit that referenced this pull request Mar 5, 2026
…rg.openrewrite.recipe-rewrite-recipe-bom-3.25.0

* upstream/main: (35 commits)
  Chore: add dependency-management.md (#15278)
  Chore(deps): Bump dev.langchain4j:langchain4j-bom in /versions (#15277)
  New Crowdin updates (#15274)
  Chore(deps): Bump actions/upload-artifact from 6 to 7 (#15271)
  Chore(deps): Bump actions/download-artifact from 7 to 8 (#15270)
  Chore(deps): Bump docker/login-action from 3 to 4 (#15268)
  Fix threading issues in citations relations tab (#15233)
  Fix: Citavi XML importer now preserves citation keys (#14658) (#15257)
  Preserve no break spaces in Latex to Unicode conversion (#15174)
  Fix: open javafx.scene.control.skin to controlsfx (#15260)
  Reduce complexity in dependencies setup (restore) (#15194)
  New translations jabref_en.properties (French) (#15256)
  Fix: exception dialog shows up when moving sidepanel down/up (#15248)
  Implement reset for Name Display Preferences (#15136)
  Chore(deps): Bump net.bytebuddy:byte-buddy in /versions (#15252)
  Chore(deps): Bump io.zonky.test.postgres:embedded-postgres-binaries-bom (#15253)
  Chore(deps): Bump io.zonky.test:embedded-postgres in /versions (#15254)
  Chore(deps): Bump net.ltgt.errorprone from 5.0.0 to 5.1.0 in /jablib (#15251)
  New Crowdin updates (#15247)
  Refined the "Select files to import" page in "Search for unlinked local files" dialog (#15110)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good second issue Issues that involve a tour of two or three interweaved components in JabRef good third issue status: no-bot-comments status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refine "Find unlinked files" dialog

3 participants