Skip to content
Merged
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 @@ -70,6 +70,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added support for import of a Refer/BibIX file format. [#13069](https://github.com/JabRef/jabref/issues/13069)
- We added markdown rendering and copy capabilities to AI chat responses. [#12234](https://github.com/JabRef/jabref/issues/12234)
- We added a new `jabkit` command `pseudonymize` to pseudonymize the library. [#13109](https://github.com/JabRef/jabref/issues/13109)
- We moved the clear fields mechanic in the Automatic Field Editor from the edit content tab to a separate tab. [#13780](https://github.com/JabRef/jabref/issues/13780)
- We added functionality to focus running instance when trying to start a second instance. [#13129](https://github.com/JabRef/jabref/issues/13129)
- We added a "Copy Field Content" submenu to the entry context menu, allowing users to quickly copy specific field contents including Author, Journal, Date, Keywords, and Abstract fields from selected entries. [#13280](https://github.com/JabRef/jabref/pull/13280)
- We added a highlighted diff regarding changes to the Group Tree Structure of a bib file, made outside JabRef. [#11221](https://github.com/JabRef/jabref/issues/11221)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.StateManager;
import org.jabref.gui.edit.automaticfiededitor.clearcontent.ClearContentTabView;
import org.jabref.gui.edit.automaticfiededitor.copyormovecontent.CopyOrMoveFieldContentTabView;
import org.jabref.gui.edit.automaticfiededitor.editfieldcontent.EditFieldContentTabView;
import org.jabref.gui.edit.automaticfiededitor.renamefield.RenameFieldTabView;
Expand All @@ -25,6 +26,7 @@ public AutomaticFieldEditorViewModel(BibDatabase database, UndoManager undoManag
fieldEditorTabs.addAll(
new EditFieldContentTabView(database, stateManager),
new CopyOrMoveFieldContentTabView(database, stateManager),
new ClearContentTabView(stateManager),
new RenameFieldTabView(database, stateManager)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.jabref.gui.edit.automaticfiededitor.clearcontent;

import java.util.Set;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;

import org.jabref.gui.StateManager;
import org.jabref.gui.edit.automaticfiededitor.AbstractAutomaticFieldEditorTabView;
import org.jabref.gui.edit.automaticfiededitor.AutomaticFieldEditorTab;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.field.Field;

import com.airhacks.afterburner.views.ViewLoader;
import com.tobiasdiez.easybind.EasyBind;

import static org.jabref.gui.util.FieldsUtil.FIELD_STRING_CONVERTER;

public class ClearContentTabView extends AbstractAutomaticFieldEditorTabView implements AutomaticFieldEditorTab {

@FXML private ComboBox<Field> fieldComboBox;
@FXML private CheckBox showOnlySetFieldsCheckBox;
@FXML private Button clearButton;
private final StateManager stateManager;
private ClearContentViewModel viewModel;

public ClearContentTabView(StateManager stateManager) {
this.stateManager = stateManager;
ViewLoader.view(this)
.root(this)
.load();
}

@FXML
public void initialize() {
viewModel = new ClearContentViewModel(stateManager);

fieldComboBox.setConverter(FIELD_STRING_CONVERTER);
fieldComboBox.getItems().setAll(viewModel.getAllFields());
if (!fieldComboBox.getItems().isEmpty()) {
fieldComboBox.getSelectionModel().selectFirst();
}

EasyBind.subscribe(showOnlySetFieldsCheckBox.selectedProperty(), selected -> {
Set<Field> items = selected ? viewModel.getSetFieldsOnly()
: viewModel.getAllFields();
fieldComboBox.getItems().setAll(items);
if (!items.isEmpty()) {
fieldComboBox.getSelectionModel().selectFirst();
}
});

clearButton.disableProperty().bind(fieldComboBox.valueProperty().isNull());

Platform.runLater(fieldComboBox::requestFocus);
}

@FXML
private void onClear() {
Field chosen = fieldComboBox.getValue();
if (chosen != null) {
viewModel.clearField(chosen);
}
}

@Override
public String getTabName() {
return Localization.lang("Clear content");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.jabref.gui.edit.automaticfiededitor.clearcontent;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.jabref.gui.StateManager;
import org.jabref.gui.edit.automaticfiededitor.LastAutomaticFieldEditorEdit;
import org.jabref.gui.undo.NamedCompoundEdit;
import org.jabref.gui.undo.UndoableFieldChange;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.FieldFactory;

public class ClearContentViewModel {
public static final int TAB_INDEX = 2;
Copy link
Member

Choose a reason for hiding this comment

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

TAB_INDEX constant is horrible, but is actually consistent with the current design of the dialog. Neccessary for the fancy infotext about affected files after applying the change. Should be refactored in the future, but for now, it can just stay.

private final StateManager stateManager;

public ClearContentViewModel(StateManager stateManager) {
this.stateManager = stateManager;
}

public Set<Field> getAllFields() {
return FieldFactory.getAllFieldsWithOutInternal();
}

public Set<Field> getSetFieldsOnly() {
return stateManager.getSelectedEntries().stream()
.flatMap(entry -> entry.getFields().stream()
.filter(f -> entry.getField(f).isPresent() && !entry.getField(f).get().isBlank()))
.collect(Collectors.toCollection(LinkedHashSet::new));
}

public void clearField(Field field) {
NamedCompoundEdit edits = new NamedCompoundEdit("CLEAR_SELECTED_FIELD");
List<BibEntry> selected = stateManager.getSelectedEntries();
int affectedEntriesCount = 0;
for (BibEntry entry : selected) {
Optional<String> oldFieldValue = entry.getField(field);
if (oldFieldValue.isPresent()) {
entry.clearField(field)
.ifPresent(change -> edits.addEdit(new UndoableFieldChange(change)));
affectedEntriesCount++;
}
}

if (edits.hasEdits()) {
edits.end();
}
stateManager.setLastAutomaticFieldEditorEdit(
new LastAutomaticFieldEditorEdit(
affectedEntriesCount,
TAB_INDEX,
edits)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

public class EditFieldContentTabView extends AbstractAutomaticFieldEditorTabView {
public Button appendValueButton;
public Button clearFieldButton;
public Button setValueButton;

@FXML
private ComboBox<Field> fieldComboBox;

Expand Down Expand Up @@ -64,15 +64,14 @@ public void initialize() {
fieldComboBox.getSelectionModel().selectFirst();

fieldComboBox.valueProperty().bindBidirectional(viewModel.selectedFieldProperty());
EasyBind.listen(fieldComboBox.getEditor().textProperty(), observable -> fieldComboBox.commitValue());
EasyBind.listen(fieldComboBox.getEditor().textProperty(), _ -> fieldComboBox.commitValue());

fieldValueTextField.textProperty().bindBidirectional(viewModel.fieldValueProperty());

overwriteFieldContentCheckBox.selectedProperty().bindBidirectional(viewModel.overwriteFieldContentProperty());

appendValueButton.disableProperty().bind(viewModel.canAppendProperty().not());
setValueButton.disableProperty().bind(viewModel.fieldValidationStatus().validProperty().not());
clearFieldButton.disableProperty().bind(viewModel.fieldValidationStatus().validProperty().not());
overwriteFieldContentCheckBox.disableProperty().bind(viewModel.fieldValidationStatus().validProperty().not());

Platform.runLater(() -> visualizer.initVisualization(viewModel.fieldValidationStatus(), fieldComboBox, true));
Expand All @@ -88,11 +87,6 @@ void appendToFieldValue() {
viewModel.appendToFieldValue();
}

@FXML
void clearField() {
viewModel.clearSelectedField();
}

@FXML
void setFieldValue() {
viewModel.setFieldValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,6 @@ public BooleanBinding canAppendProperty() {
return canAppend;
}

public void clearSelectedField() {
NamedCompoundEdit clearFieldEdit = new NamedCompoundEdit("CLEAR_SELECTED_FIELD");
int affectedEntriesCount = 0;
for (BibEntry entry : selectedEntries) {
Optional<String> oldFieldValue = entry.getField(selectedField.get());
if (oldFieldValue.isPresent()) {
entry.clearField(selectedField.get())
.ifPresent(fieldChange -> clearFieldEdit.addEdit(new UndoableFieldChange(fieldChange)));
affectedEntriesCount++;
}
}

if (clearFieldEdit.hasEdits()) {
clearFieldEdit.end();
}
stateManager.setLastAutomaticFieldEditorEdit(new LastAutomaticFieldEditorEdit(
affectedEntriesCount,
TAB_INDEX,
clearFieldEdit
));
}

public void setFieldValue() {
NamedCompoundEdit setFieldEdit = new NamedCompoundEdit("CHANGE_SELECTED_FIELD");
String toSetFieldValue = fieldValue.getValue();
Expand Down Expand Up @@ -150,10 +128,6 @@ public Field getSelectedField() {
return selectedFieldProperty().get();
}

public String getFieldValue() {
return fieldValue.get();
}

public StringProperty fieldValueProperty() {
return fieldValue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void initialize() {
fieldComboBox.setConverter(FIELD_STRING_CONVERTER);

fieldComboBox.valueProperty().bindBidirectional(viewModel.selectedFieldProperty());
EasyBind.listen(fieldComboBox.getEditor().textProperty(), observable -> fieldComboBox.commitValue());
EasyBind.listen(fieldComboBox.getEditor().textProperty(), _ -> fieldComboBox.commitValue());

renameButton.disableProperty().bind(viewModel.canRenameProperty().not());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.icon.JabRefIconView?>
<fx:root prefHeight="400.0" prefWidth="600.0" type="VBox" styleClass="edit-field-content-pane"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.jabref.gui.edit.automaticfiededitor.clearcontent.ClearContentTabView">
<GridPane hgap="8.0" prefWidth="568.0" vgap="8.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<HBox alignment="BOTTOM_LEFT"
maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
minHeight="-Infinity" minWidth="-Infinity"
GridPane.hgrow="ALWAYS" GridPane.rowSpan="2" GridPane.vgrow="ALWAYS">
<Label styleClass="sectionHeader"
text="%Clear field content for selected entries">
<HBox.margin>
<Insets bottom="10.0"/>
</HBox.margin>
</Label>
</HBox>
<HBox spacing="8.0" GridPane.rowIndex="2" GridPane.columnSpan="3" GridPane.hgrow="ALWAYS">
<ComboBox fx:id="fieldComboBox" prefHeight="36.0" editable="true" HBox.hgrow="ALWAYS" />
<CheckBox fx:id="showOnlySetFieldsCheckBox" text="%Show only set fields"/>
</HBox>
<HBox spacing="8.0" GridPane.rowIndex="3" GridPane.columnSpan="3">
<Button fx:id="clearButton" text="%Clear field content" onAction="#onClear">
<graphic>
<JabRefIconView glyph="DELETE_ENTRY"/>
</graphic>
</Button>
</HBox>
</GridPane>
</fx:root>
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.icon.JabRefIconView?>
<fx:root type="VBox" styleClass="edit-field-content-pane" xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1"
<fx:root type="VBox" styleClass="edit-field-content-pane"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.jabref.gui.edit.automaticfiededitor.editfieldcontent.EditFieldContentTabView">
<Label styleClass="sectionHeader" text="%Edit field content for selected entries"/>
<HBox styleClass="actions">
Expand All @@ -23,11 +22,5 @@
<Button fx:id="appendValueButton" mnemonicParsing="false" onAction="#appendToFieldValue"
text="%Append"/>
<Separator orientation="VERTICAL"/>
<Button fx:id="clearFieldButton" mnemonicParsing="false" onAction="#clearField"
text="%Clear field content">
<graphic>
<JabRefIconView glyph="DELETE_ENTRY"/>
</graphic>
</Button>
</HBox>
</fx:root>
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,6 @@ void setup() {
editFieldContentViewModel = new EditFieldContentViewModel(bibDatabase, List.of(entryA, entryB), stateManager);
}

@Test
void clearSelectedFieldShouldClearFieldContentEvenWhenOverwriteFieldContentIsNotEnabled() {
editFieldContentViewModel.selectedFieldProperty().set(StandardField.YEAR);
editFieldContentViewModel.overwriteFieldContentProperty().set(false);
editFieldContentViewModel.clearSelectedField();

assertEquals(Optional.empty(), entryA.getField(StandardField.YEAR));
}

@Test
void clearSelectedFieldShouldDoNothingWhenFieldDoesntExistOrIsEmpty() {
editFieldContentViewModel.selectedFieldProperty().set(StandardField.FILE);
editFieldContentViewModel.clearSelectedField();

assertEquals(Optional.empty(), entryA.getField(StandardField.FILE));
}

@Test
void setFieldValueShouldNotDoAnythingIfOverwriteFieldContentIsNotEnabled() {
editFieldContentViewModel.overwriteFieldContentProperty().set(false);
Expand Down
Loading