Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add group dialog directory structure option #10991

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv

### Added

- We added a "Directory structure" RadioButton that automatically creates a group with the same name as an imported local directory. [#10930](https://github.com/JabRef/jabref/issues/10930)
- We converted the "Custom API key" list to a table to be more accessible. [#10926](https://github.com/JabRef/jabref/issues/10926)
- We added a "refresh" button for the LaTeX citations tab in the entry editor. [#10584](https://github.com/JabRef/jabref/issues/10584)
- We added the possibility to show the BibTeX source in the [web search](https://docs.jabref.org/collect/import-using-online-bibliographic-database) import screen. [#560](https://github.com/koppor/jabref/issues/560)
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/jabref/gui/groups/GroupDialog.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@
<Tooltip text="%Group containing entries cited in a given TeX file"/>
</tooltip>
</RadioButton>
<RadioButton fx:id="dirRadioButton" toggleGroup="$type" wrapText="true"
text="%Directory structure">
<tooltip>
<Tooltip text="%Directory tree"/>
</tooltip>
</RadioButton>
</VBox>
<Separator orientation="VERTICAL"/>
<StackPane HBox.hgrow="ALWAYS">
Expand Down Expand Up @@ -175,6 +181,13 @@
<Button onAction="#texGroupBrowse" text="%Browse" prefHeight="30.0"/>
</HBox>
</VBox>
<VBox visible="${dirRadioButton.selected}">
<Label text="%Directory"/>
<HBox spacing="10.0">
<TextField fx:id="dirGroupFilePath" HBox.hgrow="ALWAYS" prefHeight="30.0"/>
<Button onAction="#dirGroupBrowse" text="%Browse" prefHeight="30.0"/>
</HBox>
</VBox>
</StackPane>
</HBox>
</VBox>
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/jabref/gui/groups/GroupDialogView.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public class GroupDialogView extends BaseDialog<AbstractGroup> {
@FXML private RadioButton searchRadioButton;
@FXML private RadioButton autoRadioButton;
@FXML private RadioButton texRadioButton;
@FXML private RadioButton dirRadioButton;

// Option Groups
@FXML private TextField keywordGroupSearchTerm;
Expand All @@ -101,6 +102,7 @@ public class GroupDialogView extends BaseDialog<AbstractGroup> {
@FXML private TextField autoGroupPersonsField;

@FXML private TextField texGroupFilePath;
@FXML private TextField dirGroupFilePath;

private final EnumMap<GroupHierarchyType, String> hierarchyText = new EnumMap<>(GroupHierarchyType.class);
private final EnumMap<GroupHierarchyType, String> hierarchyToolTip = new EnumMap<>(GroupHierarchyType.class);
Expand Down Expand Up @@ -201,6 +203,7 @@ public void initialize() {
searchRadioButton.selectedProperty().bindBidirectional(viewModel.typeSearchProperty());
autoRadioButton.selectedProperty().bindBidirectional(viewModel.typeAutoProperty());
texRadioButton.selectedProperty().bindBidirectional(viewModel.typeTexProperty());
dirRadioButton.selectedProperty().bindBidirectional(viewModel.typeDirProperty());

keywordGroupSearchTerm.textProperty().bindBidirectional(viewModel.keywordGroupSearchTermProperty());
keywordGroupSearchField.textProperty().bindBidirectional(viewModel.keywordGroupSearchFieldProperty());
Expand Down Expand Up @@ -237,6 +240,7 @@ public void initialize() {
autoGroupPersonsField.textProperty().bindBidirectional(viewModel.autoGroupPersonsFieldProperty());

texGroupFilePath.textProperty().bindBidirectional(viewModel.texGroupFilePathProperty());
dirGroupFilePath.textProperty().bindBidirectional(viewModel.dirGroupFilePathProperty());

validationVisualizer.setDecoration(new IconValidationDecorator());
Platform.runLater(() -> {
Expand All @@ -249,6 +253,7 @@ public void initialize() {
validationVisualizer.initVisualization(viewModel.keywordSearchTermEmptyValidationStatus(), keywordGroupSearchTerm);
validationVisualizer.initVisualization(viewModel.keywordFieldEmptyValidationStatus(), keywordGroupSearchField);
validationVisualizer.initVisualization(viewModel.texGroupFilePathValidatonStatus(), texGroupFilePath);
validationVisualizer.initVisualization(viewModel.dirGroupFilePathValidatonStatus(), dirGroupFilePath);
nameField.requestFocus();
});

Expand Down Expand Up @@ -277,6 +282,11 @@ private void texGroupBrowse() {
viewModel.texGroupBrowse();
}

@FXML
private void dirGroupBrowse() {
viewModel.dirGroupBrowse();
}

@FXML
private void openIconPicker() {
ObservableList<Ikon> ikonList = FXCollections.observableArrayList();
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.jabref.gui.DialogService;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.auxparser.DefaultAuxParser;
import org.jabref.logic.groups.DefaultGroupsFactory;
Expand Down Expand Up @@ -76,6 +77,7 @@ public class GroupDialogViewModel {
private final BooleanProperty typeSearchProperty = new SimpleBooleanProperty();
private final BooleanProperty typeAutoProperty = new SimpleBooleanProperty();
private final BooleanProperty typeTexProperty = new SimpleBooleanProperty();
private final BooleanProperty typeDirProperty = new SimpleBooleanProperty();

// Option Groups
private final StringProperty keywordGroupSearchTermProperty = new SimpleStringProperty("");
Expand All @@ -94,6 +96,7 @@ public class GroupDialogViewModel {
private final StringProperty autoGroupPersonsFieldProperty = new SimpleStringProperty("");

private final StringProperty texGroupFilePathProperty = new SimpleStringProperty("");
private final StringProperty dirGroupFilePathProperty = new SimpleStringProperty("");

private Validator nameValidator;
private Validator nameContainsDelimiterValidator;
Expand All @@ -104,6 +107,7 @@ public class GroupDialogViewModel {
private Validator searchRegexValidator;
private Validator searchSearchTermEmptyValidator;
private Validator texGroupFilePathValidator;
private Validator dirGroupFilePathValidator;
private CompositeValidator validator;

private final DialogService dialogService;
Expand Down Expand Up @@ -252,6 +256,21 @@ private void setupValidation() {
},
ValidationMessage.error(Localization.lang("Please provide a valid aux file.")));

dirGroupFilePathValidator = new FunctionBasedValidator<>(
dirGroupFilePathProperty,
input -> {
if (StringUtil.isBlank(input)) {
return false;
} else {
Path inputPath = getAbsoluteTexGroupPath(input);
if (!inputPath.isAbsolute() || !Files.isDirectory(inputPath)) {
return false;
}
return Files.isDirectory(inputPath);
}
},
ValidationMessage.error(Localization.lang("Please provide a valid directory")));

typeSearchProperty.addListener((obs, _oldValue, isSelected) -> {
if (isSelected) {
validator.addValidators(searchRegexValidator, searchSearchTermEmptyValidator);
Expand All @@ -276,6 +295,14 @@ private void setupValidation() {
}
});

typeDirProperty.addListener((obs, oldValue, isSelected) -> {
if (isSelected) {
validator.addValidators(dirGroupFilePathValidator);
} else {
validator.removeValidators(dirGroupFilePathValidator);
}
});

validator.addValidators(nameValidator,
nameContainsDelimiterValidator,
sameNameValidator);
Expand Down Expand Up @@ -371,6 +398,14 @@ public AbstractGroup resultConverter(ButtonType button) {
new DefaultAuxParser(new BibDatabase()),
fileUpdateMonitor,
currentDatabase.getMetaData());
} else if (typeDirProperty.getValue()) {
resultingGroup = TexGroup.create(
groupName,
groupHierarchySelectedProperty.getValue(),
Path.of(dirGroupFilePathProperty.getValue().trim()),
new DefaultAuxParser(new BibDatabase()),
fileUpdateMonitor,
currentDatabase.getMetaData());
}

if (resultingGroup != null) {
Expand Down Expand Up @@ -491,6 +526,20 @@ public void texGroupBrowse() {
));
}

public void dirGroupBrowse() {
DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder()
.withInitialDirectory(currentDatabase.getMetaData()
.getLatexFileDirectory(preferencesService.getFilePreferences().getUserAndHost())
.orElse(FileUtil.getInitialDirectory(currentDatabase, preferencesService.getFilePreferences().getWorkingDirectory()))).build();
dialogService.showDirectorySelectionDialog(directoryDialogConfiguration)
.ifPresent(file -> {
nameProperty.setValue(file.getFileName().toString());
dirGroupFilePathProperty.setValue(
FileUtil.relativize(file.toAbsolutePath(), getFileDirectoriesAsPaths()).toString()
);
});
}

private List<Path> getFileDirectoriesAsPaths() {
List<Path> fileDirs = new ArrayList<>();
MetaData metaData = currentDatabase.getMetaData();
Expand Down Expand Up @@ -539,6 +588,10 @@ public ValidationStatus texGroupFilePathValidatonStatus() {
return texGroupFilePathValidator.getValidationStatus();
}

public ValidationStatus dirGroupFilePathValidatonStatus() {
return dirGroupFilePathValidator.getValidationStatus();
}

public StringProperty nameProperty() {
return nameProperty;
}
Expand Down Expand Up @@ -587,6 +640,10 @@ public BooleanProperty typeTexProperty() {
return typeTexProperty;
}

public BooleanProperty typeDirProperty() {
return typeDirProperty;
}

public StringProperty keywordGroupSearchTermProperty() {
return keywordGroupSearchTermProperty;
}
Expand Down Expand Up @@ -638,4 +695,8 @@ public StringProperty autoGroupPersonsFieldProperty() {
public StringProperty texGroupFilePathProperty() {
return texGroupFilePathProperty;
}

public StringProperty dirGroupFilePathProperty() {
return dirGroupFilePathProperty;
}
}
3 changes: 3 additions & 0 deletions src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2109,6 +2109,9 @@ Collect\ by=Collect by
Explicit\ selection=Explicit selection
Specified\ keywords=Specified keywords
Cited\ entries=Cited entries
Directory\ structure=Directory structure
Directory\ tree=Directory tree
Please\ provide\ a\ valid\ directory=Please provide a valid directory
Please\ provide\ a\ valid\ aux\ file.=Please provide a valid aux file.
Keyword\ delimiter=Keyword delimiter
Hierarchical\ keyword\ delimiter=Hierarchical keyword delimiter
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/org/jabref/gui/groups/GroupDialogViewModelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,47 @@ void validateExistingRelativePath() throws Exception {
assertTrue(viewModel.texGroupFilePathValidatonStatus().isValid());
}

@Test
void validateExistingDirectoryAbsolutePath() throws Exception {
var directory = temporaryFolder.resolve("directory").toAbsolutePath();

Files.createDirectory(directory);
when(metaData.getLatexFileDirectory(any(String.class))).thenReturn(Optional.of(temporaryFolder));

viewModel.dirGroupFilePathProperty().setValue(directory.toString());
assertTrue(viewModel.dirGroupFilePathValidatonStatus().isValid());
}

@Test
void validateNonExistingDirectoryAbsolutePath() {
var notDirectory = temporaryFolder.resolve("notdirectory").toAbsolutePath();
viewModel.dirGroupFilePathProperty().setValue(notDirectory.toString());
assertFalse(viewModel.dirGroupFilePathValidatonStatus().isValid());
}

@Test
void validateNonExistingDirectoryAsFileAbsolutePath() throws Exception {
var file = temporaryFolder.resolve("file").toAbsolutePath(); // File without .extension

Files.createFile(file);
when(metaData.getLatexFileDirectory(any(String.class))).thenReturn(Optional.of(temporaryFolder));

viewModel.dirGroupFilePathProperty().setValue(file.toString());
assertFalse(viewModel.dirGroupFilePathValidatonStatus().isValid());
}

@Test
void validateExistingDirectoryRelativePath() throws Exception {
var directory = Path.of("directory");

// The file needs to exist
Files.createDirectory(temporaryFolder.resolve(directory));
when(metaData.getLatexFileDirectory(any(String.class))).thenReturn(Optional.of(temporaryFolder));

viewModel.dirGroupFilePathProperty().setValue(directory.toString());
assertTrue(viewModel.dirGroupFilePathValidatonStatus().isValid());
}

@Test
void hierarchicalContextFromGroup() throws Exception {
GroupHierarchyType groupHierarchyType = GroupHierarchyType.INCLUDING;
Expand Down
Loading