Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
7f72e38
Add context menu for multi-file entries (#12567)
w0nderfu11 Aug 20, 2025
5c3fc14
Apply OpenRewrite recipes
w0nderfu11 Aug 20, 2025
740cc4a
Fixed test and optimized imports
w0nderfu11 Aug 20, 2025
afa12f1
merge for problems with lib
w0nderfu11 Aug 20, 2025
02797f7
fixed library and space in catch block
w0nderfu11 Aug 20, 2025
8924487
Runned rewrite, fixed catch block and optimize logic of test ContextM…
w0nderfu11 Aug 20, 2025
f3f91e7
Fix tests: initialize JavaFX toolkit; replace empty catch with meanin…
w0nderfu11 Aug 20, 2025
e6ef784
Fix tests: added space after {
w0nderfu11 Aug 20, 2025
f31fd86
Trigger CI
w0nderfu11 Aug 20, 2025
650bd9e
Trigger CI
w0nderfu11 Aug 20, 2025
8d86798
Update jabgui/src/test/java/org/jabref/gui/copyfiles/CopyMultipleFile…
w0nderfu11 Sep 1, 2025
181587a
refactor(gui/contextmenu): restore Strategy pattern for linked files …
w0nderfu11 Sep 2, 2025
665b069
feat(gui): multi-file context menu for linked files
w0nderfu11 Sep 5, 2025
d100fba
merge: origin/feat/multi-file-context-menu into feat/multi-file-conte…
w0nderfu11 Sep 5, 2025
56bccb1
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 5, 2025
a926f87
Merge branch 'main' into feat/multi-file-context-menu
Siedlerchr Sep 8, 2025
ccc2863
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 9, 2025
7adefe6
Address review: Optional/map, non-null params, logger, tests, menus
w0nderfu11 Sep 9, 2025
60e9c86
Merge branch 'feat/multi-file-context-menu' of https://github.com/w0n…
w0nderfu11 Sep 9, 2025
a99821d
fixed tests
w0nderfu11 Sep 9, 2025
a9b9e4f
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 10, 2025
ed78f30
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 11, 2025
a429df9
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 12, 2025
d1933c2
Update jabgui/src/main/java/org/jabref/gui/copyfiles/CopySingleFileAc…
w0nderfu11 Sep 12, 2025
8c6387d
Update jabgui/src/main/java/org/jabref/gui/copyfiles/CopySingleFileAc…
w0nderfu11 Sep 12, 2025
259e37c
wip: local changes before style sync
w0nderfu11 Sep 17, 2025
44f084b
Fix styles
calixtus Sep 18, 2025
a9f21ae
Merge remote-tracking branch 'upstream/main' into fork/w0nderfu11/fea…
calixtus Sep 18, 2025
79dec90
Final style and CI checks
w0nderfu11 Sep 18, 2025
7d5b3c6
docs: sync http-server howto; jablib: sync jspecify @Nullable from up…
w0nderfu11 Sep 18, 2025
514232c
Naming convention
calixtus Sep 18, 2025
e3eb1df
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 19, 2025
31af228
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 19, 2025
0832ba0
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 19, 2025
ace2a3c
feat(gui): multi-file context menu for linked files (sync on upstream…
w0nderfu11 Sep 19, 2025
d36dbac
sync of branches
w0nderfu11 Sep 19, 2025
d245346
Fix LOGGER naming (convension)
w0nderfu11 Sep 19, 2025
791caba
fixed submodules
w0nderfu11 Sep 19, 2025
88702c8
Fix submodules
w0nderfu11 Sep 19, 2025
19c71d4
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 19, 2025
668eedb
chore: reset submodules to upstream/main (csl-styles, csl-locales)
w0nderfu11 Sep 19, 2025
374d697
Fix submodules
w0nderfu11 Sep 19, 2025
91bc19a
Fix submodules
w0nderfu11 Sep 19, 2025
5b002c9
Merge branch 'main' into feat/multi-file-context-menu
w0nderfu11 Sep 23, 2025
f5dbd10
Fix test classes
w0nderfu11 Sep 24, 2025
796507f
Merge remote-tracking branch 'origin/feat/multi-file-context-menu' in…
w0nderfu11 Sep 24, 2025
b73504a
Fix test classes V2 (forget to update MultiSelectionMenuBuilderTest a…
w0nderfu11 Sep 24, 2025
c327bd8
Apply JSpecify annotations
calixtus Sep 24, 2025
8fc6724
Fixed bot's cases and fixed submodules
w0nderfu11 Sep 25, 2025
19109fb
Fixed conflict and cases of bot
w0nderfu11 Sep 25, 2025
9cac8be
Merge remote-tracking branch 'upstream/main' into fork/w0nderfu11/fea…
calixtus Sep 26, 2025
dc7ad22
Fix submodules
calixtus Sep 26, 2025
ecbf18a
Migrate to JSpecify annotations
calixtus Sep 26, 2025
c2427d1
Fix wording
calixtus Sep 26, 2025
189f823
Changed assert methods for readability
calixtus Sep 26, 2025
e281d32
interface ---< class, deleted variable
w0nderfu11 Sep 29, 2025
730ddd3
Refactored ContextMenuFactoryTest for readability
calixtus Oct 3, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added support for finding CSL-Styles based on their short title (e.g. apa instead of "american psychological association"). [#13728](https://github.com/JabRef/jabref/pull/13728)
- We added a field for the latest ICORE conference ranking lookup on the General Tab. [#13476](https://github.com/JabRef/jabref/issues/13476)
- We added BibLaTeX datamodel validation support in order to improve error message quality in entries' fields validation. [#13318](https://github.com/JabRef/jabref/issues/13318)
- We added support for managing multiple linked files via the entry context menu. [#12567](https://github.com/JabRef/jabref/issues/12567)
- We added more supported formats of CAYW endpoint of HTTP server. [#13578](https://github.com/JabRef/jabref/issues/13578)
- We added chronological navigation for entries in each library. [#6352](https://github.com/JabRef/jabref/issues/6352)
- We added support for using Medline/Pubmed fetcher with an API key. [#11296](https://github.com/JabRef/jabref/issues/11296#issuecomment-3289005011)
Expand Down
16 changes: 8 additions & 8 deletions jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ public enum StandardActions implements Action {
EDIT_EXISTING_STUDY(Localization.lang("Manage study definition")),

OPEN_DATABASE_FOLDER(Localization.lang("Reveal in file explorer")),
OPEN_FOLDER(Localization.lang("Open folder"), Localization.lang("Open folder"), IconTheme.JabRefIcons.FOLDER, KeyBinding.OPEN_FOLDER),
OPEN_FILE(Localization.lang("Open file"), Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE),
OPEN_FOLDER(Localization.lang("Open folder(s)"), Localization.lang("Open folder"), IconTheme.JabRefIcons.FOLDER, KeyBinding.OPEN_FOLDER),
OPEN_FILE(Localization.lang("Open file(s)"), Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE),
OPEN_CONSOLE(Localization.lang("Open terminal here"), Localization.lang("Open terminal here"), IconTheme.JabRefIcons.CONSOLE, KeyBinding.OPEN_CONSOLE),
COPY_LINKED_FILES(Localization.lang("Copy linked files to folder...")),
COPY_DOI(Localization.lang("Copy DOI")),
Expand Down Expand Up @@ -169,16 +169,16 @@ public enum StandardActions implements Action {
SET_FILE_LINKS(Localization.lang("Automatically set file links"), KeyBinding.AUTOMATICALLY_LINK_FILES),

EDIT_FILE_LINK(Localization.lang("Edit"), IconTheme.JabRefIcons.EDIT, KeyBinding.OPEN_CLOSE_ENTRY_EDITOR),
DOWNLOAD_FILE(Localization.lang("Download file"), IconTheme.JabRefIcons.DOWNLOAD_FILE),
REDOWNLOAD_FILE(Localization.lang("Redownload file"), IconTheme.JabRefIcons.DOWNLOAD_FILE),
DOWNLOAD_FILE(Localization.lang("Download file(s)"), IconTheme.JabRefIcons.DOWNLOAD_FILE),
REDOWNLOAD_FILE(Localization.lang("Redownload file(s)"), IconTheme.JabRefIcons.DOWNLOAD_FILE),
RENAME_FILE_TO_PATTERN(Localization.lang("Rename file to defined pattern"), IconTheme.JabRefIcons.AUTO_RENAME),
RENAME_FILE_TO_NAME(Localization.lang("Rename files to configured filename format pattern"), IconTheme.JabRefIcons.RENAME, KeyBinding.REPLACE_STRING),
MOVE_FILE_TO_FOLDER(Localization.lang("Move file to file directory"), IconTheme.JabRefIcons.MOVE_TO_FOLDER),
RENAME_FILE_TO_NAME(Localization.lang("Rename file(s) to configured filename format pattern"), IconTheme.JabRefIcons.RENAME, KeyBinding.REPLACE_STRING),
MOVE_FILE_TO_FOLDER(Localization.lang("Move file(s) to file directory"), IconTheme.JabRefIcons.MOVE_TO_FOLDER),
MOVE_FILE_TO_FOLDER_AND_RENAME(Localization.lang("Move file to file directory and rename file")),
COPY_FILE_TO_FOLDER(Localization.lang("Copy linked file to folder..."), IconTheme.JabRefIcons.COPY_TO_FOLDER, KeyBinding.COPY),
COPY_FILE_TO_FOLDER(Localization.lang("Copy linked file(s) to folder..."), IconTheme.JabRefIcons.COPY_TO_FOLDER, KeyBinding.COPY),
REMOVE_LINK(Localization.lang("Remove link"), IconTheme.JabRefIcons.REMOVE_LINK),
REMOVE_LINKS(Localization.lang("Remove links"), IconTheme.JabRefIcons.REMOVE_LINK),
DELETE_FILE(Localization.lang("Permanently delete local file"), IconTheme.JabRefIcons.DELETE_FILE, KeyBinding.DELETE_ENTRY),
DELETE_FILE(Localization.lang("Permanently delete local file(s)"), IconTheme.JabRefIcons.DELETE_FILE, KeyBinding.DELETE_ENTRY),

HELP(Localization.lang("Online help"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP),
HELP_GROUPS(Localization.lang("Open Help page"), IconTheme.JabRefIcons.HELP, KeyBinding.HELP),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package org.jabref.gui.copyfiles;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;

import javafx.beans.Observable;
import javafx.beans.binding.Bindings;

import org.jabref.gui.DialogService;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.logic.FilePreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.LinkedFile;

public class CopyLinkedFilesAction extends SimpleCommand {

private final List<LinkedFile> linkedFiles;
private final DialogService dialogService;
private final BibDatabaseContext databaseContext;
private final FilePreferences filePreferences;

private final BiFunction<Path, Path, Path> resolvePathFilename = (dir, file) -> dir.resolve(file.getFileName());

public CopyLinkedFilesAction(LinkedFile linkedFile,
DialogService dialogService,
BibDatabaseContext databaseContext,
FilePreferences filePreferences) {
this(List.of(linkedFile), dialogService, databaseContext, filePreferences);
}

public CopyLinkedFilesAction(Collection<LinkedFile> linkedFiles,
DialogService dialogService,
BibDatabaseContext databaseContext,
FilePreferences filePreferences) {
this.linkedFiles = new ArrayList<>(linkedFiles);
this.dialogService = dialogService;
this.databaseContext = databaseContext;
this.filePreferences = filePreferences;

this.executable.bind(Bindings.createBooleanBinding(
() -> this.linkedFiles.stream().anyMatch(this::isLocalExisting),
dependencies(this.linkedFiles)));
}

@Override
public void execute() {
DirectoryDialogConfiguration dirDialogConfiguration = new DirectoryDialogConfiguration.Builder()
.withInitialDirectory(filePreferences.getWorkingDirectory())
.build();

Optional<Path> exportDir = dialogService.showDirectorySelectionDialog(dirDialogConfiguration);
if (exportDir.isEmpty()) {
return;
}

int copiedFiles = 0;
int failedCount = 0;

for (LinkedFile file : linkedFiles) {
Optional<Path> srcOpt = file.findIn(databaseContext, filePreferences);
if (srcOpt.isEmpty()) {
failedCount++;
continue;
}

Path src = srcOpt.get();
Path dst = resolvePathFilename.apply(exportDir.get(), src);

if (FileUtil.copyFile(src, dst, false)) {
copiedFiles++;
} else {
failedCount++;
}
}

String title = Localization.lang("Copy linked file");
String target = exportDir.map(Path::toString).orElse("");

if (linkedFiles.size() == 1) {
if (copiedFiles == 1) {
dialogService.notify(Localization.lang("Successfully copied %0 file(s) to %1.", copiedFiles, target));
} else {
dialogService.showErrorDialogAndWait(
title,
Localization.lang("Could not copy file to %0, maybe the file is already existing?", target));
}
} else {
if (failedCount == 0) {
dialogService.notify(Localization.lang("Successfully copied %0 file(s) to %1.", copiedFiles, target));
} else {
dialogService.showErrorDialogAndWait(
title,
Localization.lang("Copied %0 file(s). Failed: %1", copiedFiles, failedCount));
}
}
}

private boolean isLocalExisting(LinkedFile lf) {
return !lf.isOnlineLink() && lf.findIn(databaseContext, filePreferences).isPresent();
}

private static Observable[] dependencies(Collection<LinkedFile> files) {
return files.stream().map(LinkedFile::linkProperty).toArray(Observable[]::new);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,6 @@ public void acceptAsLinked() {

public Observable[] getObservables() {
List<Observable> observables = new ArrayList<>(Arrays.asList(linkedFile.getObservables()));
observables.add(downloadOngoing);
observables.add(downloadProgress);
observables.add(isAutomaticallyFound);
return observables.toArray(new Observable[0]);
}
Expand Down Expand Up @@ -442,9 +440,15 @@ public void redownload() {
throw new UnsupportedOperationException("In order to download the file, the source url has to be an online link");
}

DownloadLinkedFileAction downloadLinkedFileAction = getDownloadLinkedFileAction();
downloadProgress.bind(downloadLinkedFileAction.downloadProgress());
downloadLinkedFileAction.execute();
}

private DownloadLinkedFileAction getDownloadLinkedFileAction() {
String fileName = Path.of(linkedFile.getLink()).getFileName().toString();

DownloadLinkedFileAction downloadLinkedFileAction = new DownloadLinkedFileAction(
return new DownloadLinkedFileAction(
databaseContext,
entry,
linkedFile,
Expand All @@ -455,8 +459,6 @@ public void redownload() {
taskExecutor,
fileName,
true);
downloadProgress.bind(downloadLinkedFileAction.downloadProgress());
downloadLinkedFileAction.execute();
}

public void download(boolean keepHtmlLink) {
Expand Down
Loading
Loading