From 376760c24f7e7e46a189e557618321f5e05e8f4c Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Thu, 24 Mar 2022 01:47:04 +0100 Subject: [PATCH 001/122] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f719dbf8742..a62785db499 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,6 @@ For IntelliJ IDEA, just import the project via a Gradle Import by pointing at th ## Sponsoring -JabRef development is powered by YourKit Java Profiler [![YourKit Java Profiler](https://www.yourkit.com/images/yk_logo.png)](https://www.yourkit.com/java/profiler/) +JabRef development is by YourKit Java Profiler [![YourKit Java Profiler](https://www.yourkit.com/images/yk_logo.png)](https://www.yourkit.com/java/profiler/) [JabRef]: https://www.jabref.org From 3c3fb02787dd450fd505aa65c80eaafccffd917a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 7 Jun 2022 13:49:26 +0100 Subject: [PATCH 002/122] Fix Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 677fcc7e75c..78a90d2785d 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,6 @@ For IntelliJ IDEA, just import the project via a Gradle Import by pointing at th ## Sponsoring -JabRef development is by YourKit Java Profiler [![YourKit Java Profiler](https://www.yourkit.com/images/yk_logo.png)](https://www.yourkit.com/java/profiler/) +JabRef development is by powered YourKit Java Profiler [![YourKit Java Profiler](https://www.yourkit.com/images/yk_logo.png)](https://www.yourkit.com/java/profiler/) [JabRef]: https://www.jabref.org From 1b12b75954895349917d8248e2df139465ad5011 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 01:58:15 +0100 Subject: [PATCH 003/122] Create grid cells for rendering field name and field value --- src/main/java/module-info.java | 1 + .../newmergedialog/ThreeWayMerge.java | 4 + .../newmergedialog/cell/AbstractCell.java | 69 +++++++++++ .../newmergedialog/cell/FieldNameCell.java | 7 ++ .../newmergedialog/cell/FieldValueCell.java | 108 ++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 751863df3e1..68a5a571fbd 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -117,6 +117,7 @@ requires net.harawata.appdirs; requires org.eclipse.jgit; + requires wellbehavedfx; uses org.eclipse.jgit.transport.SshSessionFactory; uses org.eclipse.jgit.lib.GpgSigner; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java new file mode 100644 index 00000000000..bf2f9e651af --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java @@ -0,0 +1,4 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +public class ThreeWayMerge { +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java new file mode 100644 index 00000000000..a1583987921 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java @@ -0,0 +1,69 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Insets; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; + +/** + * + */ +public abstract class AbstractCell extends HBox { + private final StringProperty text = new SimpleStringProperty(); + private final ObjectProperty backgroundTone = new SimpleObjectProperty<>(); + + public AbstractCell(String text, BackgroundTone backgroundTone) { + backgroundToneProperty().addListener(invalidated -> setBackground(Background.fill(getBackgroundTone().color()))); + setPadding(new Insets(8)); + + setText(text); + setBackgroundTone(backgroundTone); + } + + public AbstractCell(String text) { + this(text, BackgroundTone.DARK); + } + + public String getText() { + return textProperty().get(); + } + + public StringProperty textProperty() { + return text; + } + + public void setText(String text) { + textProperty().set(text); + } + + public void setBackgroundTone(BackgroundTone backgroundTone) { + backgroundToneProperty().set(backgroundTone); + } + + public BackgroundTone getBackgroundTone() { + return backgroundToneProperty().get(); + } + + public ObjectProperty backgroundToneProperty() { + return backgroundTone; + } + + public enum BackgroundTone { + LIGHT(Color.web("#F7F7F7")), DARK(Color.web("#FAFAFA")); + private final Color color; + + BackgroundTone(Color color) { + this.color = color; + } + + public Color color() { + return color; + } + } +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java new file mode 100644 index 00000000000..af73de11961 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -0,0 +1,7 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +/** + * A non-editable cell that contains the name of some field + */ +public class FieldNameCell { +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java new file mode 100644 index 00000000000..7a91515987d --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -0,0 +1,108 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.event.Event; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.Toggle; +import javafx.scene.control.ToggleGroup; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Background; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; + +import org.fxmisc.flowless.VirtualizedScrollPane; +import org.fxmisc.richtext.StyleClassedTextArea; +import org.fxmisc.wellbehaved.event.InputMap; +import org.fxmisc.wellbehaved.event.Nodes; + +import static org.fxmisc.wellbehaved.event.EventPattern.anyOf; +import static org.fxmisc.wellbehaved.event.EventPattern.eventType; +import static org.fxmisc.wellbehaved.event.EventPattern.mousePressed; + +/** + * A non-editable and selectable field cell that contains the value of some field + */ +public class FieldValueCell extends AbstractCell implements Toggle { + private final ObjectProperty toggleGroup = new SimpleObjectProperty<>(); + private final BooleanProperty selected = new SimpleBooleanProperty(); + + private final StyleClassedTextArea label = new StyleClassedTextArea(); + + private final VirtualizedScrollPane scrollPane = new VirtualizedScrollPane<>(label); + + public FieldValueCell(String text, BackgroundTone backgroundTone) { + super(text, backgroundTone); + } + + public FieldValueCell(String text) { + super(text); + } + + private void initialize() { + initializeLabel(); + initializeSelectionBox(); + } + + private void initializeLabel() { + label.setEditable(false); + label.setBackground(Background.fill(Color.TRANSPARENT)); + label.appendText(textProperty().get()); + label.setAutoHeight(true); + label.setWrapText(true); + + preventTextSelectionViaMouseEvents(); + } + + private void initializeSelectionBox() { + } + + private void initializeScrollPane() { + HBox.setHgrow(scrollPane, Priority.ALWAYS); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + } + + private void preventTextSelectionViaMouseEvents() { + InputMap preventSelection = InputMap.consume( + anyOf(eventType(MouseEvent.MOUSE_DRAGGED), + eventType(MouseEvent.DRAG_DETECTED), + eventType(MouseEvent.MOUSE_ENTERED), + mousePressed().unless(e -> e.getClickCount() == 1) + ) + ); + Nodes.addInputMap(label, preventSelection); + } + + @Override + public ToggleGroup getToggleGroup() { + return toggleGroupProperty().get(); + } + + @Override + public void setToggleGroup(ToggleGroup toggleGroup) { + toggleGroupProperty().set(toggleGroup); + } + + @Override + public ObjectProperty toggleGroupProperty() { + return toggleGroup; + } + + @Override + public boolean isSelected() { + return selectedProperty().get(); + } + + @Override + public void setSelected(boolean selected) { + selectedProperty().set(selected); + } + + @Override + public BooleanProperty selectedProperty() { + return selected; + } +} From 4c073d3b9a66ed3b8449677321ceb40758adfa3c Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:02:59 +0100 Subject: [PATCH 004/122] Implement FieldNameCell and define style class --- .../newmergedialog/cell/FieldNameCell.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index af73de11961..ce3f33059ba 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -1,7 +1,29 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; +import javafx.scene.control.Label; + /** * A non-editable cell that contains the name of some field */ -public class FieldNameCell { +public class FieldNameCell extends AbstractCell { + public static final String DEFAULT_STYLE_CLASS = "field-name"; + private final Label label = new Label(); + + public FieldNameCell(String text, BackgroundTone backgroundTone) { + super(text, backgroundTone); + } + + public FieldNameCell(String text) { + super(text); + } + + private void initialize() { + getStyleClass().add(DEFAULT_STYLE_CLASS); + initializeLabel(); + getChildren().add(label); + } + + private void initializeLabel() { + label.textProperty().bind(textProperty()); + } } From 6359058217bb40c0f0a7075f04a6eef6a6cfff7b Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:05:04 +0100 Subject: [PATCH 005/122] Make FieldValueCell selectable - Defined style class - Defined pseudo class for the selected state - Created the selection box UI --- .../newmergedialog/cell/FieldValueCell.java | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 7a91515987d..0188de09b13 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -1,50 +1,82 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; +import javafx.css.PseudoClass; import javafx.event.Event; +import javafx.geometry.Insets; +import javafx.geometry.Pos; import javafx.scene.control.ScrollPane; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.scene.input.MouseEvent; +import javafx.scene.input.ScrollEvent; import javafx.scene.layout.Background; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import org.fxmisc.flowless.VirtualizedScrollPane; import org.fxmisc.richtext.StyleClassedTextArea; import org.fxmisc.wellbehaved.event.InputMap; import org.fxmisc.wellbehaved.event.Nodes; +import org.kordamp.ikonli.javafx.FontIcon; +import org.kordamp.ikonli.materialdesign2.MaterialDesignC; import static org.fxmisc.wellbehaved.event.EventPattern.anyOf; import static org.fxmisc.wellbehaved.event.EventPattern.eventType; import static org.fxmisc.wellbehaved.event.EventPattern.mousePressed; /** - * A non-editable and selectable field cell that contains the value of some field + * A readonly, selectable field cell that contains the value of some field */ public class FieldValueCell extends AbstractCell implements Toggle { + public static final String SELECTION_BOX_STYLE_CLASS = "selection-box"; + private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected"); private final ObjectProperty toggleGroup = new SimpleObjectProperty<>(); - private final BooleanProperty selected = new SimpleBooleanProperty(); - private final StyleClassedTextArea label = new StyleClassedTextArea(); private final VirtualizedScrollPane scrollPane = new VirtualizedScrollPane<>(label); + private final BooleanProperty selected = new BooleanPropertyBase() { + @Override + public Object getBean() { + return FieldValueCell.class; + } + + @Override + public String getName() { + return "selected"; + } + + @Override + protected void invalidated() { + pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, get()); + } + }; + private final HBox selectionBox = new HBox(); + private final VBox checkmarkLayout = new VBox(); public FieldValueCell(String text, BackgroundTone backgroundTone) { super(text, backgroundTone); + initialize(); } public FieldValueCell(String text) { super(text); + initialize(); } private void initialize() { + initializeScrollPane(); initializeLabel(); initializeSelectionBox(); + textProperty().addListener(invalidated -> setUserData(getText())); + + selectionBox.getChildren().addAll(label, checkmarkLayout); + getChildren().setAll(selectionBox); } private void initializeLabel() { @@ -53,11 +85,25 @@ private void initializeLabel() { label.appendText(textProperty().get()); label.setAutoHeight(true); label.setWrapText(true); + // TODO: Set cursor to Cursor.HAND preventTextSelectionViaMouseEvents(); + + label.prefHeightProperty().bind(label.totalHeightEstimateProperty().orElseConst(-1d)); + + label.addEventFilter(ScrollEvent.SCROLL, e -> { + e.consume(); + FieldValueCell.this.fireEvent(e.copyFor(e.getSource(), FieldValueCell.this)); + }); } private void initializeSelectionBox() { + selectionBox.getStyleClass().add(SELECTION_BOX_STYLE_CLASS); + HBox.setHgrow(selectionBox, Priority.ALWAYS); + + checkmarkLayout.getChildren().setAll(new FontIcon(MaterialDesignC.CHECK)); + checkmarkLayout.setPadding(new Insets(1, 0, 0, 0)); + checkmarkLayout.setAlignment(Pos.TOP_RIGHT); } private void initializeScrollPane() { @@ -105,4 +151,14 @@ public void setSelected(boolean selected) { public BooleanProperty selectedProperty() { return selected; } + + @Override + public void setUserData(Object value) { + super.setText((String) value); + } + + @Override + public Object getUserData() { + return super.getText(); + } } From d45f4a13b98f74ebeff45228b53e9b4b45e54bc6 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:06:12 +0100 Subject: [PATCH 006/122] Implement MergedFieldCell for rendering editable merged field value --- .../newmergedialog/cell/MergedFieldCell.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java new file mode 100644 index 00000000000..4bce784c13d --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java @@ -0,0 +1,51 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.input.ScrollEvent; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; + +import org.fxmisc.richtext.StyleClassedTextArea; + +public class MergedFieldCell extends AbstractCell { + private static final String DEFAULT_STYLE_CLASS = "merged-field"; + + private final StyleClassedTextArea textArea = new StyleClassedTextArea(); + + public MergedFieldCell(String text, BackgroundTone backgroundTone) { + super(text, backgroundTone); + initialize(); + } + + public MergedFieldCell(String text) { + super(text); + initialize(); + } + + private void initialize() { + getStyleClass().add(DEFAULT_STYLE_CLASS); + initializeTextArea(); + getChildren().add(textArea); + } + + private void initializeTextArea() { + bindTextAreaValue(); + + setAlignment(Pos.CENTER); + textArea.setWrapText(true); + textArea.setAutoHeight(true); + textArea.setPadding(new Insets(8)); + HBox.setHgrow(textArea, Priority.ALWAYS); + + textArea.addEventFilter(ScrollEvent.SCROLL, e -> { + e.consume(); + MergedFieldCell.this.fireEvent(e.copyFor(e.getSource(), MergedFieldCell.this)); + }); + } + + private void bindTextAreaValue() { + textArea.replaceText(textProperty().get()); + textProperty().addListener(((observable, oldValue, newValue) -> textArea.replaceText(newValue))); + } +} From 7e226e2711d59f24b5e4e91d7699a5e68dfaed52 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:07:56 +0100 Subject: [PATCH 007/122] Change MergedFieldCell text based on the selected FieldValueCell --- .../newmergedialog/FieldRowController.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java new file mode 100644 index 00000000000..a25709c1e23 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -0,0 +1,40 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +import javafx.beans.property.ReadOnlyStringProperty; +import javafx.scene.control.ToggleGroup; + +import org.jabref.gui.mergeentries.newmergedialog.cell.AbstractCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; +import org.jabref.model.strings.StringUtil; + +public class FieldRowController { + private final FieldNameCell fieldNameCell; + private final FieldValueCell leftValueCell; + private final FieldValueCell rightValueCell; + private final MergedFieldCell mergedValueCell; + + private final ToggleGroup toggleGroup = new ToggleGroup(); + + public FieldRowController(String fieldName, String leftValue, String rightValue, AbstractCell.BackgroundTone backgroundTone) { + fieldNameCell = new FieldNameCell(fieldName, backgroundTone); + leftValueCell = new FieldValueCell(leftValue, backgroundTone); + rightValueCell = new FieldValueCell(rightValue, backgroundTone); + mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, backgroundTone); + + toggleGroup.getToggles().addAll(leftValueCell, rightValueCell); + toggleGroup.selectToggle(StringUtil.isNullOrEmpty(leftValue) ? rightValueCell : leftValueCell); + toggleGroup.selectedToggleProperty().addListener(invalidated -> { + mergedValueCell.setText((String) toggleGroup.getSelectedToggle().getUserData()); + }); + } + + public String getMergedValue() { + return mergedValueProperty().getValue(); + } + + public ReadOnlyStringProperty mergedValueProperty() { + return mergedValueCell.textProperty(); + } +} From fb137cbcf0adfd8e4804532bede83db8bb6b3608 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:08:31 +0100 Subject: [PATCH 008/122] Style MergedFieldCell --- .../mergeentries/newmergedialog/cell/AbstractCell.java | 4 ++-- .../gui/mergeentries/newmergedialog/newmergedialog.css | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java index a1583987921..9cb60f44ba5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java @@ -6,8 +6,6 @@ import javafx.beans.property.StringProperty; import javafx.geometry.Insets; import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; import javafx.scene.layout.HBox; import javafx.scene.paint.Color; @@ -24,6 +22,8 @@ public AbstractCell(String text, BackgroundTone backgroundTone) { setText(text); setBackgroundTone(backgroundTone); + // TODO: Remove this when cells are added to the grid pane, and add the stylesheet to the root layout instead. + getStylesheets().add("../newmergedialog.css"); } public AbstractCell(String text) { diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css new file mode 100644 index 00000000000..e0f4741e73d --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css @@ -0,0 +1,10 @@ +.merged-field { + -fx-border-color: #dfdfdf; + -fx-border-width: 0 0 0 1.5; +} + +.merged-field .styled-text-area { + -fx-background-radius: 5; + -fx-border-radius: 5; + -fx-border-color: #dfdfdf +} From 8d76e405a1d39a1bad0daefdaa661a552e9808bb Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:52:52 +0100 Subject: [PATCH 009/122] Rename ThreeWayMerge css stylesheet --- .../newmergedialog/ThreeWayMergeView.css | 62 +++++++++++++++++++ .../newmergedialog/cell/AbstractCell.java | 4 +- .../newmergedialog/newmergedialog.css | 10 --- 3 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css delete mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css new file mode 100644 index 00000000000..c95470b32dc --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -0,0 +1,62 @@ +.merged-field { + -fx-border-color: #dfdfdf; + -fx-border-width: 0 0 0 1.5; +} + +.merged-field .styled-text-area { + -fx-background-radius: 5; + -fx-border-radius: 5; + -fx-border-color: #dfdfdf +} + +.field-value:selected .selection-box { + -fx-background-color: -jr-transparent-green; + -fx-border-color: -jr-green; +} + +.field-value .selection-box { + -fx-background-color: #0000; + -fx-padding: 8; + -fx-border-width: 1; + -fx-border-color: #219b3800; + -fx-border-style: SOLID; + -fx-border-radius: 8; + -fx-background-radius: 8; +} + +.field-value .label { + -fx-font-size: 13; +} + +.field-value .text-area { + -fx-font-size: 13; +} + +.field-value .checkmark { + -fx-icon-size: 13; + -fx-icon-color: #25b060; + -fx-opacity: 0%; +} + +.field-value:selected .checkmark { + -fx-icon-size: 14; + -fx-icon-color: -jr-green; + -fx-opacity: 100%; +} + +.unchanged { + +} + +.addition { + -rtfx-background-color: rgba(35, 225, 96, 0.26); +} + +.deletion { + -rtfx-background-color: rgba(248, 62, 68, 0.26); +} + + +* { + -jr-transparent-green: rgba(16, 172, 132, 0.15); +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java index 9cb60f44ba5..b57a14bce1f 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java @@ -23,7 +23,7 @@ public AbstractCell(String text, BackgroundTone backgroundTone) { setText(text); setBackgroundTone(backgroundTone); // TODO: Remove this when cells are added to the grid pane, and add the stylesheet to the root layout instead. - getStylesheets().add("../newmergedialog.css"); + getStylesheets().add("../ThreeWayMergeView.css"); } public AbstractCell(String text) { @@ -55,7 +55,7 @@ public ObjectProperty backgroundToneProperty() { } public enum BackgroundTone { - LIGHT(Color.web("#F7F7F7")), DARK(Color.web("#FAFAFA")); + LIGHT(Color.web("#FEFEFE")), DARK(Color.web("#EFEFEF")); private final Color color; BackgroundTone(Color color) { diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css deleted file mode 100644 index e0f4741e73d..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/newmergedialog.css +++ /dev/null @@ -1,10 +0,0 @@ -.merged-field { - -fx-border-color: #dfdfdf; - -fx-border-width: 0 0 0 1.5; -} - -.merged-field .styled-text-area { - -fx-background-radius: 5; - -fx-border-radius: 5; - -fx-border-color: #dfdfdf -} From 98eea59084403d5308e5a20b255bf4d97d317824 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:54:00 +0100 Subject: [PATCH 010/122] Create HeadView for rendering left, right and merged entry headers --- .../gui/mergeentries/newmergedialog/HeaderView.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java new file mode 100644 index 00000000000..ea7049cd857 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java @@ -0,0 +1,10 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +import javafx.scene.layout.HBox; + +public class HeaderView extends HBox { + public HeaderView() { + setPrefHeight(50d); + setStyle("-fx-background-color: #878787"); + } +} From 3187365bf701cc188dfb24dc7eefa495b7402ee3 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 02:54:41 +0100 Subject: [PATCH 011/122] Fix typo --- .../gui/mergeentries/newmergedialog/cell/FieldNameCell.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index ce3f33059ba..7730b4d43ab 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -11,10 +11,12 @@ public class FieldNameCell extends AbstractCell { public FieldNameCell(String text, BackgroundTone backgroundTone) { super(text, backgroundTone); + initialize(); } public FieldNameCell(String text) { super(text); + initialize(); } private void initialize() { From d00bab32ad508dc45d3ff58d79b1d56e4a3d5959 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:00:17 +0100 Subject: [PATCH 012/122] Fix a bug that causes the selection of both left and right field cells simultaneously. - Added a style class for field value cell --- .../mergeentries/newmergedialog/cell/FieldValueCell.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 0188de09b13..a2d0b1c2118 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -34,7 +34,9 @@ * A readonly, selectable field cell that contains the value of some field */ public class FieldValueCell extends AbstractCell implements Toggle { + public static final String DEFAULT_STYLE_CLASS = "field-value"; public static final String SELECTION_BOX_STYLE_CLASS = "selection-box"; + private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected"); private final ObjectProperty toggleGroup = new SimpleObjectProperty<>(); private final StyleClassedTextArea label = new StyleClassedTextArea(); @@ -54,6 +56,9 @@ public String getName() { @Override protected void invalidated() { pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, get()); + + ToggleGroup group = getToggleGroup(); + group.selectToggle(FieldValueCell.this); } }; private final HBox selectionBox = new HBox(); @@ -70,6 +75,7 @@ public FieldValueCell(String text) { } private void initialize() { + getStyleClass().add(DEFAULT_STYLE_CLASS); initializeScrollPane(); initializeLabel(); initializeSelectionBox(); From 02c040416da5ed56447bdbf2110b334073a4064f Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:05:20 +0100 Subject: [PATCH 013/122] Prevent the selection of FieldValueCell when it's disabled - A cell is disabled when it's either empty or not visible e.g. when the left cell spans 2 columns, right cell is disabled because it's not visible - Added comments --- .../mergeentries/newmergedialog/cell/FieldValueCell.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index a2d0b1c2118..a8f4265e8be 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -80,6 +80,11 @@ private void initialize() { initializeLabel(); initializeSelectionBox(); textProperty().addListener(invalidated -> setUserData(getText())); + setOnMouseClicked(e -> { + if (!isDisabled()) { + setSelected(true); + } + }); selectionBox.getChildren().addAll(label, checkmarkLayout); getChildren().setAll(selectionBox); @@ -91,12 +96,13 @@ private void initializeLabel() { label.appendText(textProperty().get()); label.setAutoHeight(true); label.setWrapText(true); - // TODO: Set cursor to Cursor.HAND + // Workarounds preventTextSelectionViaMouseEvents(); label.prefHeightProperty().bind(label.totalHeightEstimateProperty().orElseConst(-1d)); + // Fix text area consuming scroll events before they rich the outer scrollable label.addEventFilter(ScrollEvent.SCROLL, e -> { e.consume(); FieldValueCell.this.fireEvent(e.copyFor(e.getSource(), FieldValueCell.this)); From 05527c6de821ba8a7ca8a195064a7873c259ee15 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:06:33 +0100 Subject: [PATCH 014/122] Display a copy icon inside FieldValueCell --- .../mergeentries/newmergedialog/cell/FieldValueCell.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index a8f4265e8be..33426a4e847 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -86,7 +86,7 @@ private void initialize() { } }); - selectionBox.getChildren().addAll(label, checkmarkLayout); + selectionBox.getChildren().addAll(scrollPane, checkmarkLayout); getChildren().setAll(selectionBox); } @@ -113,7 +113,9 @@ private void initializeSelectionBox() { selectionBox.getStyleClass().add(SELECTION_BOX_STYLE_CLASS); HBox.setHgrow(selectionBox, Priority.ALWAYS); - checkmarkLayout.getChildren().setAll(new FontIcon(MaterialDesignC.CHECK)); + FontIcon checkmarkCircleIcon = FontIcon.of(MaterialDesignC.CONTENT_COPY); + checkmarkCircleIcon.getStyleClass().add("checkmark-icon"); + checkmarkLayout.getChildren().setAll(checkmarkCircleIcon); checkmarkLayout.setPadding(new Insets(1, 0, 0, 0)); checkmarkLayout.setAlignment(Pos.TOP_RIGHT); } From b6db346f3a27f21971853e6565583b59ee40204b Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:08:26 +0100 Subject: [PATCH 015/122] Disable left or right FieldValueCell when it's empty - Added getters for row cells --- .../newmergedialog/FieldRowController.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index a25709c1e23..117f1d83ce5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -28,6 +28,12 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, toggleGroup.selectedToggleProperty().addListener(invalidated -> { mergedValueCell.setText((String) toggleGroup.getSelectedToggle().getUserData()); }); + + if (StringUtil.isNullOrEmpty(leftValue)) { + leftValueCell.setDisable(true); + } else if (StringUtil.isNullOrEmpty(rightValue)) { + rightValueCell.setDisable(true); + } } public String getMergedValue() { @@ -37,4 +43,20 @@ public String getMergedValue() { public ReadOnlyStringProperty mergedValueProperty() { return mergedValueCell.textProperty(); } + + public FieldNameCell getFieldNameCell() { + return fieldNameCell; + } + + public FieldValueCell getLeftValueCell() { + return leftValueCell; + } + + public FieldValueCell getRightValueCell() { + return rightValueCell; + } + + public MergedFieldCell getMergedValueCell() { + return mergedValueCell; + } } From fa97b5949cf623bb432910076b1ebe938d161cf6 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:09:33 +0100 Subject: [PATCH 016/122] Update Base CSS style --- src/main/java/org/jabref/gui/Base.css | 66 ++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 3a25f9afc25..6a49557ebbe 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1,5 +1,68 @@ -.root { +.merged-field { + -fx-border-color: #dfdfdf; + -fx-border-width: 0 0 0 1.5; +} + +.merged-field .styled-text-area { + -fx-background-radius: 3; + -fx-border-radius: 3; + -fx-border-color: #dfdfdf; + -fx-background-color: white; +} + +.field-value:selected .selection-box { + -fx-background-color: derive(-jr-accent, 20%); + -fx-border-color: -jr-accent; +} + +.field-value .selection-box { + -fx-background-color: #0000; + -fx-padding: 8; + -fx-border-width: 1; + -fx-border-color: #0000; + -fx-border-radius: 8; + -fx-background-radius: 8; +} + +.field-value .label { + -fx-font-size: 13; +} + +.field-value .text-area { + -fx-font-size: 13; +} + +.field-value .checkmark-icon { + -fx-icon-size: 16; + -fx-icon-color: -jr-gray-2; + -fx-opacity: 100%; +} +.field-value:disabled .checkmark-icon { + -fx-opacity: 0%; +} + +.field-value:selected .checkmark-icon { + -fx-icon-size: 16; + -fx-icon-color: derive(-jr-accent, -30%);; + -fx-opacity: 100%; +} + +.unchanged { + +} + +.addition { + -rtfx-background-color: rgba(35, 225, 96, 0.26); +} + +.deletion { + -rtfx-background-color: rgba(248, 62, 68, 0.26); +} + + +.root { + -jr-transparent-green: rgba(16, 172, 132, 0.12); /* The theme color and some derived colors from it are used for icons, tab-headers, marking of selected inputs and hover colors for the main menu. It generally defines the look of JabRef. The highlighting colors below should @@ -9,6 +72,7 @@ /* This theme is the original JabRef dark blue color */ -jr-theme: #50618F; -jr-accent: #a3b7e6; + -jr-transparent-accent: rgba(163, 183, 230, 0.16); -jr-selected: -jr-accent; -jr-checked: -jr-theme; -jr-hover: #0002; From 52b56042a936f99018b277d03c07c0f5032299ff Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:11:02 +0100 Subject: [PATCH 017/122] Design and implement the merge toolbox - It will be added to the top of the merge dialog --- .../toolbox/ThreeWayMergeToolbox.fxml | 27 +++ .../toolbox/ThreeWayMergeToolbox.java | 179 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml new file mode 100644 index 00000000000..ccb22cb511e --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java new file mode 100644 index 00000000000..7f6b50beb88 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java @@ -0,0 +1,179 @@ +package org.jabref.gui.mergeentries.newmergedialog.toolbox; + +import java.util.Arrays; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.FXML; +import javafx.scene.control.ComboBox; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.AnchorPane; +import javafx.util.StringConverter; + +import com.airhacks.afterburner.views.ViewLoader; +import com.tobiasdiez.easybind.EasyBind; +import com.tobiasdiez.easybind.EasyBinding; + +public class ThreeWayMergeToolbox extends AnchorPane { + @FXML + private RadioButton compareCharactersRadioButtons; + + @FXML + private RadioButton compareWordsRadioButton; + + @FXML + private ToggleGroup diffCompareMethodToggleGroup; + + @FXML + private ComboBox diffViewComboBox; + + @FXML + private ComboBox plainTextOrDiffComboBox; + + private final ObjectProperty diffCompareMethod = new SimpleObjectProperty<>(); + private EasyBinding showDiff; + + public ThreeWayMergeToolbox() { + ViewLoader.view(this) + .root(this) + .load(); + } + + @FXML + public void initialize() { + showDiff = EasyBind.map(plainTextOrDiffComboBox.valueProperty(), plainTextOrDiff -> plainTextOrDiff == PlainTextOrDiff.Diff); + + plainTextOrDiffComboBox.getItems().addAll(PlainTextOrDiff.values()); + plainTextOrDiffComboBox.getSelectionModel().select(PlainTextOrDiff.PLAIN_TEXT); + plainTextOrDiffComboBox.setConverter(new StringConverter<>() { + @Override + public String toString(PlainTextOrDiff plainTextOrDiff) { + return plainTextOrDiff.getValue(); + } + + @Override + public PlainTextOrDiff fromString(String string) { + return PlainTextOrDiff.fromString(string); + } + }); + + diffViewComboBox.disableProperty().bind(notShowDiffProperty()); + diffViewComboBox.getItems().addAll(DiffView.values()); + diffViewComboBox.getSelectionModel().select(DiffView.UNIFIED); + diffViewComboBox.setConverter(new StringConverter<>() { + @Override + public String toString(DiffView diffView) { + return diffView.getValue(); + } + + @Override + public DiffView fromString(String string) { + return DiffView.fromString(string); + } + }); + + compareWordsRadioButton.disableProperty().bind(notShowDiffProperty()); + compareCharactersRadioButtons.disableProperty().bind(notShowDiffProperty()); + + diffCompareMethodToggleGroup.selectedToggleProperty().addListener((observable -> { + if (diffCompareMethodToggleGroup.getSelectedToggle().equals(compareCharactersRadioButtons)) { + diffCompareMethod.set(DiffCompareMethod.CHARS); + } else { + diffCompareMethod.set(DiffCompareMethod.WORDS); + } + })); + + diffCompareMethodToggleGroup.selectToggle(compareWordsRadioButton); + } + + private void initializeDiffViewComboBox() { + diffViewComboBox.getSelectionModel().select(DiffView.UNIFIED); + diffViewComboBox.setConverter(new StringConverter<>() { + @Override + public String toString(DiffView diffView) { + return diffView.getValue(); + } + + @Override + public DiffView fromString(String string) { + return DiffView.fromString(string); + } + }); + } + + public ReadOnlyObjectProperty diffViewProperty() { + return diffViewComboBox.valueProperty(); + } + + public DiffView getDiffView() { + return diffViewProperty().get(); + } + + public EasyBinding showDiffProperty() { + return showDiff; + } + + public EasyBinding notShowDiffProperty() { + return showDiffProperty().map(showDiff -> !showDiff); + } + + public Boolean isShowDiffEnabled() { + return showDiffProperty().get(); + } + + public ObjectProperty diffCompareMethodProperty() { + return diffCompareMethod; + } + + public DiffCompareMethod getDiffCompareMethod() { + return diffCompareMethodProperty().get(); + } + + public enum PlainTextOrDiff { + PLAIN_TEXT("Plain Text"), Diff("Show Diff"); + + private final String value; + + PlainTextOrDiff(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static PlainTextOrDiff fromString(String str) { + return Arrays.stream(values()) + .filter(plainTextOrDiff -> plainTextOrDiff.getValue().equals(str)) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + } + + public enum DiffView { + UNIFIED("Unified View"), + SPLIT("Split View"); + private final String value; + + DiffView(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static DiffView fromString(String str) { + return Arrays.stream(values()) + .filter(diffView -> diffView.getValue().equals(str)) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + } + + public enum DiffCompareMethod { + WORDS, CHARS + } +} From b2e5d38ed3ac2c99fc5454df990985e3a166b01a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:13:35 +0100 Subject: [PATCH 018/122] Implement the initial prototype of the new merge UI - Renamed ThreeWayMerge.java to ThreeWayMergeView --- .../newmergedialog/ThreeWayMerge.java | 4 - .../newmergedialog/ThreeWayMergeView.java | 78 +++++++++++++ .../ThreeWayMergeViewModel.java | 105 ++++++++++++++++++ 3 files changed, 183 insertions(+), 4 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java deleted file mode 100644 index bf2f9e651af..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMerge.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.jabref.gui.mergeentries.newmergedialog; - -public class ThreeWayMerge { -} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java new file mode 100644 index 00000000000..c7b0cf5b64e --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -0,0 +1,78 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.stage.Screen; + +import org.jabref.gui.mergeentries.newmergedialog.toolbox.ThreeWayMergeToolbox; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; + +import static org.jabref.gui.mergeentries.newmergedialog.cell.AbstractCell.BackgroundTone; + +public class ThreeWayMergeView extends VBox { + public static final int GRID_COLUMN_MIN_WIDTH = 100; + public static final String LEFT_DEFAULT_HEADER = "Left Entry"; + public static final String RIGHT_DEFAULT_HEADER = "Right Entry"; + private ThreeWayMergeToolbox mergeToolbox; + private HeaderView headerView; + private final ScrollPane scrollPane; + private final GridPane mergeGridPane; + + private final ThreeWayMergeViewModel viewModel; + + public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { + // getStylesheets().add("newmergedialog.css"); + viewModel = new ThreeWayMergeViewModel(leftEntry, rightEntry, leftHeader, rightHeader); + + mergeToolbox = new ThreeWayMergeToolbox(); + headerView = new HeaderView(); + scrollPane = new ScrollPane(); + scrollPane.setFitToWidth(true); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + mergeGridPane = new GridPane(); + initializeMergeGridPane(); + + scrollPane.setContent(mergeGridPane); + getChildren().addAll(mergeToolbox, headerView, scrollPane); + + this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.78); + this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.95); + } + + public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry) { + this(leftEntry, rightEntry, LEFT_DEFAULT_HEADER, RIGHT_DEFAULT_HEADER); + } + + private void initializeMergeGridPane() { + ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); + fieldNameColumnConstraints.setHgrow(Priority.NEVER); + + ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + ColumnConstraints rightEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + ColumnConstraints mergedEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + + leftEntryColumnConstraints.setHgrow(Priority.ALWAYS); + rightEntryColumnConstraints.setHgrow(Priority.ALWAYS); + mergedEntryColumnConstraints.setHgrow(Priority.ALWAYS); + + mergeGridPane.getColumnConstraints().addAll(fieldNameColumnConstraints, leftEntryColumnConstraints, rightEntryColumnConstraints, mergedEntryColumnConstraints); + + for (int fieldIndex = 0; fieldIndex < viewModel.allFieldsSize(); fieldIndex++) { + addFieldRow(fieldIndex); + } + } + + private void addFieldRow(int index) { + Field field = viewModel.allFields().get(index); + String leftEntryValue = viewModel.getLeftEntry().getField(field).orElse(""); + String rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); + BackgroundTone backgroundTone = index % 2 == 0 ? BackgroundTone.DARK : BackgroundTone.LIGHT; + + FieldRowController fieldRow = new FieldRowController(field.getDisplayName(), leftEntryValue, rightEntryValue, backgroundTone); + mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); + } +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java new file mode 100644 index 00000000000..47912bf9f5b --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java @@ -0,0 +1,105 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import org.jabref.gui.AbstractViewModel; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; + +class ThreeWayMergeViewModel extends AbstractViewModel { + + private final ObjectProperty leftEntry = new SimpleObjectProperty<>(); + private final ObjectProperty rightEntry = new SimpleObjectProperty<>(); + private final ObjectProperty mergedEntry = new SimpleObjectProperty<>(); + private final StringProperty leftHeader = new SimpleStringProperty(); + private final StringProperty rightHeader = new SimpleStringProperty(); + + private final ObservableList allFields = FXCollections.observableArrayList(); + + public ThreeWayMergeViewModel(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { + Objects.requireNonNull(leftEntry, "Left entry is required"); + Objects.requireNonNull(rightEntry, "Right entry is required"); + Objects.requireNonNull(leftHeader, "Left header entry is required"); + Objects.requireNonNull(rightHeader, "Right header is required"); + + setLeftEntry(leftEntry); + setRightEntry(rightEntry); + setLeftHeader(leftHeader); + setRightHeader(rightHeader); + + Set leftAndRightFieldsUnion = new HashSet<>(leftEntry.getFields()); + leftAndRightFieldsUnion.addAll(rightEntry.getFields()); + setAllFields(leftAndRightFieldsUnion); + } + + public StringProperty leftHeaderProperty() { + return leftHeader; + } + + public String getLeftHeader() { + return leftHeader.get(); + } + + public void setLeftHeader(String leftHeader) { + leftHeaderProperty().set(leftHeader); + } + + public StringProperty rightHeaderProperty() { + return rightHeader; + } + + public String getRightHeader() { + return rightHeaderProperty().get(); + } + + public void setRightHeader(String rightHeader) { + rightHeaderProperty().set(rightHeader); + } + + public BibEntry getLeftEntry() { + return leftEntry.get(); + } + + private void setLeftEntry(BibEntry bibEntry) { + leftEntry.set(bibEntry); + } + + public BibEntry getRightEntry() { + return rightEntry.get(); + } + + private void setRightEntry(BibEntry bibEntry) { + rightEntry.set(bibEntry); + } + + public BibEntry getMergedEntry() { + return mergedEntry.get(); + } + + public ObservableList allFields() { + return allFields; + } + + /** + * Convince method to determine the total number of fields in the union of the left and right fields. + */ + public int allFieldsSize() { + return allFields.size(); + } + + private void setAllFields(Set fields) { + allFields.clear(); + allFields.addAll(fields); + allFields.sort(Comparator.comparing(Field::getName)); + } +} From 51c6195b586ee385bd5d04e9db8bfd2d29a37a3e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:15:08 +0100 Subject: [PATCH 019/122] Use ThreeWayMergeView in MergeEntriesDialog - At this stage headers aren't updated and merged bib entry is not created --- .../org/jabref/gui/mergeentries/MergeEntriesDialog.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index 65fb71648bd..c75f89023a4 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -3,6 +3,7 @@ import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; +import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView; import org.jabref.gui.util.BaseDialog; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; @@ -10,9 +11,11 @@ public class MergeEntriesDialog extends BaseDialog { private final MergeEntries mergeEntries; + private final ThreeWayMergeView threeWayMergeView; public MergeEntriesDialog(BibEntry one, BibEntry two) { mergeEntries = new MergeEntries(one, two); + threeWayMergeView = new ThreeWayMergeView(one, two); init(); } @@ -21,14 +24,14 @@ public MergeEntriesDialog(BibEntry one, BibEntry two) { * Sets up the dialog */ private void init() { - this.getDialogPane().setContent(mergeEntries); + this.getDialogPane().setContent(threeWayMergeView); // Create buttons ButtonType replaceEntries = new ButtonType(Localization.lang("Merge entries"), ButtonBar.ButtonData.OK_DONE); this.getDialogPane().getButtonTypes().setAll(ButtonType.CANCEL, replaceEntries); this.setResultConverter(buttonType -> { if (buttonType.equals(replaceEntries)) { - return mergeEntries.getMergedEntry(); + return null; } else { return null; } From e66ae3d33710005b8358384b407dcb285fbe1ad9 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:18:58 +0100 Subject: [PATCH 020/122] Fix ThreeWayMergeView.css stylesheet not being imported --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index c7b0cf5b64e..fadc67c3f9d 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -25,7 +25,7 @@ public class ThreeWayMergeView extends VBox { private final ThreeWayMergeViewModel viewModel; public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { - // getStylesheets().add("newmergedialog.css"); + getStylesheets().add(ThreeWayMergeView.class.getResource("ThreeWayMergeView.css").toExternalForm()); viewModel = new ThreeWayMergeViewModel(leftEntry, rightEntry, leftHeader, rightHeader); mergeToolbox = new ThreeWayMergeToolbox(); From d39171e97ce1db05c17a2c96c22d374dcb45f76d Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:20:33 +0100 Subject: [PATCH 021/122] Span left field value to 2 columns when left and right field values are the same --- .../newmergedialog/FieldRowController.java | 10 ++++++++++ .../mergeentries/newmergedialog/ThreeWayMergeView.java | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 117f1d83ce5..6eebcaff257 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -59,4 +59,14 @@ public FieldValueCell getRightValueCell() { public MergedFieldCell getMergedValueCell() { return mergedValueCell; } + + public boolean hasEqualLeftAndRightValues() { + return !StringUtil.isNullOrEmpty(leftValueCell.getText()) && + !StringUtil.isNullOrEmpty(rightValueCell.getText()) && + leftValueCell.getText().equals(rightValueCell.getText()); + } + + public void deselectLeft() { + toggleGroup.getSelectedToggle().setSelected(false); + } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index fadc67c3f9d..3d3d204ce2c 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -73,6 +73,13 @@ private void addFieldRow(int index) { BackgroundTone backgroundTone = index % 2 == 0 ? BackgroundTone.DARK : BackgroundTone.LIGHT; FieldRowController fieldRow = new FieldRowController(field.getDisplayName(), leftEntryValue, rightEntryValue, backgroundTone); - mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); + + if (fieldRow.hasEqualLeftAndRightValues()) { + mergeGridPane.add(fieldRow.getFieldNameCell(), 0, index, 1, 1); + mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index, 2, 1); + mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index, 1, 1); + } else { + mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); + } } } From 6cdcec7f653ad9c1b3163cf83ad093e4b034ee21 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:23:16 +0100 Subject: [PATCH 022/122] Wrap the copy icon inside a button - Updated the selection box UI --- .../newmergedialog/cell/FieldValueCell.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 33426a4e847..69c42ab0229 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -42,6 +42,7 @@ public class FieldValueCell extends AbstractCell implements Toggle { private final StyleClassedTextArea label = new StyleClassedTextArea(); private final VirtualizedScrollPane scrollPane = new VirtualizedScrollPane<>(label); + HBox labelBox = new HBox(scrollPane); private final BooleanProperty selected = new BooleanPropertyBase() { @Override public Object getBean() { @@ -69,11 +70,6 @@ public FieldValueCell(String text, BackgroundTone backgroundTone) { initialize(); } - public FieldValueCell(String text) { - super(text); - initialize(); - } - private void initialize() { getStyleClass().add(DEFAULT_STYLE_CLASS); initializeScrollPane(); @@ -86,7 +82,7 @@ private void initialize() { } }); - selectionBox.getChildren().addAll(scrollPane, checkmarkLayout); + selectionBox.getChildren().addAll(labelBox, checkmarkLayout); getChildren().setAll(selectionBox); } @@ -113,11 +109,23 @@ private void initializeSelectionBox() { selectionBox.getStyleClass().add(SELECTION_BOX_STYLE_CLASS); HBox.setHgrow(selectionBox, Priority.ALWAYS); - FontIcon checkmarkCircleIcon = FontIcon.of(MaterialDesignC.CONTENT_COPY); - checkmarkCircleIcon.getStyleClass().add("checkmark-icon"); - checkmarkLayout.getChildren().setAll(checkmarkCircleIcon); - checkmarkLayout.setPadding(new Insets(1, 0, 0, 0)); - checkmarkLayout.setAlignment(Pos.TOP_RIGHT); + HBox.setHgrow(labelBox, Priority.ALWAYS); + labelBox.setPadding(new Insets(8)); + labelBox.setCursor(Cursor.HAND); + + FontIcon copyIcon = FontIcon.of(MaterialDesignC.CONTENT_COPY); + Button copyButton = new Button(); + copyButton.setGraphic(copyIcon); + copyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + copyButton.setStyle("-fx-border-width: 0"); + + copyIcon.getStyleClass().add("checkmark-icon"); + checkmarkLayout.getChildren().setAll(copyButton); + checkmarkLayout.setAlignment(Pos.TOP_CENTER); + checkmarkLayout.setPrefWidth(28); + + copyButton.setMaxHeight(Double.MAX_VALUE); + VBox.setVgrow(copyButton, Priority.ALWAYS); } private void initializeScrollPane() { From c19d46ae9e27e651c8b29f3045d52e96c2ef0caf Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:24:07 +0100 Subject: [PATCH 023/122] Use HAND cursor when hover over FieldValueCell --- .../gui/mergeentries/newmergedialog/cell/FieldValueCell.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 69c42ab0229..473ae014e07 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -8,6 +8,9 @@ import javafx.event.Event; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.scene.Cursor; +import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.ScrollPane; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; @@ -92,6 +95,7 @@ private void initializeLabel() { label.appendText(textProperty().get()); label.setAutoHeight(true); label.setWrapText(true); + label.setStyle("-fx-cursor: hand"); // Workarounds preventTextSelectionViaMouseEvents(); From d1983ba5914127346561243072f532e82c0db68a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:25:03 +0100 Subject: [PATCH 024/122] Move field cells styles to ThreeWayMergeView.css --- src/main/java/org/jabref/gui/Base.css | 61 ------------------- .../newmergedialog/ThreeWayMergeView.css | 36 ++++++----- 2 files changed, 17 insertions(+), 80 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 6a49557ebbe..3ec5f717cf0 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1,64 +1,3 @@ -.merged-field { - -fx-border-color: #dfdfdf; - -fx-border-width: 0 0 0 1.5; -} - -.merged-field .styled-text-area { - -fx-background-radius: 3; - -fx-border-radius: 3; - -fx-border-color: #dfdfdf; - -fx-background-color: white; -} - -.field-value:selected .selection-box { - -fx-background-color: derive(-jr-accent, 20%); - -fx-border-color: -jr-accent; -} - -.field-value .selection-box { - -fx-background-color: #0000; - -fx-padding: 8; - -fx-border-width: 1; - -fx-border-color: #0000; - -fx-border-radius: 8; - -fx-background-radius: 8; -} - -.field-value .label { - -fx-font-size: 13; -} - -.field-value .text-area { - -fx-font-size: 13; -} - - -.field-value .checkmark-icon { - -fx-icon-size: 16; - -fx-icon-color: -jr-gray-2; - -fx-opacity: 100%; -} -.field-value:disabled .checkmark-icon { - -fx-opacity: 0%; -} - -.field-value:selected .checkmark-icon { - -fx-icon-size: 16; - -fx-icon-color: derive(-jr-accent, -30%);; - -fx-opacity: 100%; -} - -.unchanged { - -} - -.addition { - -rtfx-background-color: rgba(35, 225, 96, 0.26); -} - -.deletion { - -rtfx-background-color: rgba(248, 62, 68, 0.26); -} .root { diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index c95470b32dc..dbbe6a24b1c 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -4,22 +4,21 @@ } .merged-field .styled-text-area { - -fx-background-radius: 5; - -fx-border-radius: 5; - -fx-border-color: #dfdfdf + -fx-background-radius: 3; + -fx-border-radius: 3; + -fx-border-color: #dfdfdf; + -fx-background-color: white; } .field-value:selected .selection-box { - -fx-background-color: -jr-transparent-green; - -fx-border-color: -jr-green; + -fx-background-color: derive(-jr-accent, 20%); + -fx-border-color: -jr-accent; } .field-value .selection-box { -fx-background-color: #0000; - -fx-padding: 8; -fx-border-width: 1; - -fx-border-color: #219b3800; - -fx-border-style: SOLID; + -fx-border-color: #0000; -fx-border-radius: 8; -fx-background-radius: 8; } @@ -32,15 +31,19 @@ -fx-font-size: 13; } -.field-value .checkmark { - -fx-icon-size: 13; - -fx-icon-color: #25b060; + +.field-value .checkmark-icon { + -fx-icon-size: 16; + -fx-icon-color: -jr-gray-2; + -fx-opacity: 100%; +} +.field-value:disabled .checkmark-icon { -fx-opacity: 0%; } -.field-value:selected .checkmark { - -fx-icon-size: 14; - -fx-icon-color: -jr-green; +.field-value:selected .checkmark-icon { + -fx-icon-size: 16; + -fx-icon-color: derive(-jr-accent, -30%);; -fx-opacity: 100%; } @@ -55,8 +58,3 @@ .deletion { -rtfx-background-color: rgba(248, 62, 68, 0.26); } - - -* { - -jr-transparent-green: rgba(16, 172, 132, 0.15); -} From 5d22e16ce9042ff7d77387fa6d8bdb04af6c12d2 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:25:31 +0100 Subject: [PATCH 025/122] Update merge toolbox FXML UI --- .../newmergedialog/toolbox/ThreeWayMergeToolbox.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml index ccb22cb511e..8a729b85ecb 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml @@ -10,7 +10,7 @@ - + From 23c6c9112a366ec668ceaae89dac374a001e9ac7 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:35:13 +0100 Subject: [PATCH 026/122] Implement and style HeaderCell --- .../newmergedialog/cell/HeaderCell.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java new file mode 100644 index 00000000000..d406a76cafe --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java @@ -0,0 +1,27 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import javafx.geometry.Insets; +import javafx.scene.control.Label; + +public class HeaderCell extends AbstractCell { + public static final String DEFAULT_STYLE_CLASS = "header-cell"; + private final Label label = new Label(); + + public HeaderCell(String text) { + super(text, 1); + initialize(); + } + + private void initialize() { + getStyleClass().add(DEFAULT_STYLE_CLASS); + initializeLabel(); + getChildren().add(label); + setStyle("-fx-border-width: 0 0 0.8 0; -fx-border-color: #424758"); + } + + private void initializeLabel() { + label.textProperty().bind(textProperty()); + label.setStyle("-fx-font-weight: bold; -fx-font-size: 13"); + label.setPadding(new Insets(1, 0, 1, 0)); + } +} From d2f8e2197f7c6bc705705b5a02743fa7de7aa3a1 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:00:10 +0100 Subject: [PATCH 027/122] Take row index as an argument in AbstractCell - Created odd and even pseudo classes that will update the background color of the cell when state changes. - Created NO_ROW_INDEX to be used by HeaderCell, because HeaderCell doesn't base its background color on its row position which is 0. --- .../newmergedialog/cell/AbstractCell.java | 77 +++++++++++++------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java index b57a14bce1f..10b13bd5b71 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java @@ -1,11 +1,11 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.css.PseudoClass; import javafx.geometry.Insets; -import javafx.scene.layout.Background; import javafx.scene.layout.HBox; import javafx.scene.paint.Color; @@ -13,21 +13,60 @@ * */ public abstract class AbstractCell extends HBox { + public static final String ODD_PSEUDO_CLASS = "odd"; + public static final String EVEN_PSEUDO_CLASS = "even"; + public static final int NO_ROW_NUMBER = -1; + private static final String DEFAULT_STYLE_CLASS = "field-cell"; private final StringProperty text = new SimpleStringProperty(); - private final ObjectProperty backgroundTone = new SimpleObjectProperty<>(); + private final BooleanProperty odd = new BooleanPropertyBase() { + @Override + public Object getBean() { + return AbstractCell.this; + } + + @Override + public String getName() { + return "odd"; + } + + @Override + protected void invalidated() { + pseudoClassStateChanged(PseudoClass.getPseudoClass(ODD_PSEUDO_CLASS), get()); + pseudoClassStateChanged(PseudoClass.getPseudoClass(EVEN_PSEUDO_CLASS), !get()); + } + }; + + private final BooleanProperty even = new BooleanPropertyBase() { + @Override + public Object getBean() { + return AbstractCell.this; + } + + @Override + public String getName() { + return "even"; + } + + @Override + protected void invalidated() { + pseudoClassStateChanged(PseudoClass.getPseudoClass(EVEN_PSEUDO_CLASS), get()); + pseudoClassStateChanged(PseudoClass.getPseudoClass(ODD_PSEUDO_CLASS), !get()); + } + }; + + public AbstractCell(String text, int rowIndex) { + getStyleClass().add(DEFAULT_STYLE_CLASS); + if (rowIndex != NO_ROW_NUMBER) { + if (rowIndex % 2 == 1) { + odd.setValue(true); + } else { + even.setValue(true); + } + } - public AbstractCell(String text, BackgroundTone backgroundTone) { - backgroundToneProperty().addListener(invalidated -> setBackground(Background.fill(getBackgroundTone().color()))); setPadding(new Insets(8)); setText(text); - setBackgroundTone(backgroundTone); - // TODO: Remove this when cells are added to the grid pane, and add the stylesheet to the root layout instead. - getStylesheets().add("../ThreeWayMergeView.css"); - } - - public AbstractCell(String text) { - this(text, BackgroundTone.DARK); } public String getText() { @@ -42,18 +81,6 @@ public void setText(String text) { textProperty().set(text); } - public void setBackgroundTone(BackgroundTone backgroundTone) { - backgroundToneProperty().set(backgroundTone); - } - - public BackgroundTone getBackgroundTone() { - return backgroundToneProperty().get(); - } - - public ObjectProperty backgroundToneProperty() { - return backgroundTone; - } - public enum BackgroundTone { LIGHT(Color.web("#FEFEFE")), DARK(Color.web("#EFEFEF")); private final Color color; From 329e2f9ac038145d061a6ee149c199b27b42f974 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:37:33 +0100 Subject: [PATCH 028/122] Color cell background based on its row position (odd or even) --- .../newmergedialog/cell/FieldNameCell.java | 9 ++------- .../newmergedialog/cell/FieldValueCell.java | 8 ++++++-- .../newmergedialog/cell/MergedFieldCell.java | 11 ++++------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index 7730b4d43ab..7ad435d59b1 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -9,13 +9,8 @@ public class FieldNameCell extends AbstractCell { public static final String DEFAULT_STYLE_CLASS = "field-name"; private final Label label = new Label(); - public FieldNameCell(String text, BackgroundTone backgroundTone) { - super(text, backgroundTone); - initialize(); - } - - public FieldNameCell(String text) { - super(text); + public FieldNameCell(String text, int rowIndex) { + super(text, rowIndex); initialize(); } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 473ae014e07..9927276f621 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -22,6 +22,10 @@ import javafx.scene.layout.VBox; import javafx.scene.paint.Color; +import org.jabref.gui.Globals; +import org.jabref.gui.actions.ActionFactory; +import org.jabref.logic.l10n.Localization; + import org.fxmisc.flowless.VirtualizedScrollPane; import org.fxmisc.richtext.StyleClassedTextArea; import org.fxmisc.wellbehaved.event.InputMap; @@ -68,8 +72,8 @@ protected void invalidated() { private final HBox selectionBox = new HBox(); private final VBox checkmarkLayout = new VBox(); - public FieldValueCell(String text, BackgroundTone backgroundTone) { - super(text, backgroundTone); + public FieldValueCell(String text, int rowIndex) { + super(text, rowIndex); initialize(); } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java index 4bce784c13d..7617cdd4f30 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java @@ -6,6 +6,8 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import org.jabref.gui.util.BindingsHelper; + import org.fxmisc.richtext.StyleClassedTextArea; public class MergedFieldCell extends AbstractCell { @@ -13,13 +15,8 @@ public class MergedFieldCell extends AbstractCell { private final StyleClassedTextArea textArea = new StyleClassedTextArea(); - public MergedFieldCell(String text, BackgroundTone backgroundTone) { - super(text, backgroundTone); - initialize(); - } - - public MergedFieldCell(String text) { - super(text); + public MergedFieldCell(String text, int rowIndex) { + super(text, rowIndex); initialize(); } From 3df63ae24660e79b825f1f19f32ed9cf454cdf40 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:38:53 +0100 Subject: [PATCH 029/122] Bind textArea.textProperty() and textProperty() using BindingsHelper --- .../newmergedialog/cell/MergedFieldCell.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java index 7617cdd4f30..77f170541d6 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/MergedFieldCell.java @@ -27,7 +27,10 @@ private void initialize() { } private void initializeTextArea() { - bindTextAreaValue(); + BindingsHelper.bindBidirectional(textArea.textProperty(), + textProperty(), + textArea::replaceText, + textProperty()::setValue); setAlignment(Pos.CENTER); textArea.setWrapText(true); @@ -40,9 +43,4 @@ private void initializeTextArea() { MergedFieldCell.this.fireEvent(e.copyFor(e.getSource(), MergedFieldCell.this)); }); } - - private void bindTextAreaValue() { - textArea.replaceText(textProperty().get()); - textProperty().addListener(((observable, oldValue, newValue) -> textArea.replaceText(newValue))); - } } From dd28f10737dc3ec6051fe1c233070779996ca0e4 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:40:57 +0100 Subject: [PATCH 030/122] Copy field value to Clipboard user clicks on the copy button - Renamed copy-icon style class - Used ActionFactory for creating the copy icon button --- .../cell/CopyFieldValueCommand.java | 23 +++++++++++++++++++ .../newmergedialog/cell/FieldValueCell.java | 10 ++++---- 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java new file mode 100644 index 00000000000..e18a831933b --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java @@ -0,0 +1,23 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import java.util.Objects; + +import org.jabref.gui.ClipBoardManager; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.preferences.PreferencesService; + +public class CopyFieldValueCommand extends SimpleCommand { + private final String fieldValue; + private final ClipBoardManager clipBoardManager; + + public CopyFieldValueCommand(PreferencesService preferencesService, final String fieldValue) { + Objects.requireNonNull(fieldValue, "Field value cannot be null"); + this.fieldValue = fieldValue; + this.clipBoardManager = new ClipBoardManager(preferencesService); + } + + @Override + public void execute() { + clipBoardManager.setContent(fieldValue); + } +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 9927276f621..8945cf688e5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -48,6 +48,8 @@ public class FieldValueCell extends AbstractCell implements Toggle { private final ObjectProperty toggleGroup = new SimpleObjectProperty<>(); private final StyleClassedTextArea label = new StyleClassedTextArea(); + private final ActionFactory factory = new ActionFactory(Globals.getKeyPrefs()); + private final VirtualizedScrollPane scrollPane = new VirtualizedScrollPane<>(label); HBox labelBox = new HBox(scrollPane); private final BooleanProperty selected = new BooleanPropertyBase() { @@ -65,8 +67,7 @@ public String getName() { protected void invalidated() { pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, get()); - ToggleGroup group = getToggleGroup(); - group.selectToggle(FieldValueCell.this); + getToggleGroup().selectToggle(FieldValueCell.this); } }; private final HBox selectionBox = new HBox(); @@ -122,12 +123,11 @@ private void initializeSelectionBox() { labelBox.setCursor(Cursor.HAND); FontIcon copyIcon = FontIcon.of(MaterialDesignC.CONTENT_COPY); - Button copyButton = new Button(); + Button copyButton = factory.createIconButton(() -> Localization.lang("Copy"), new CopyFieldValueCommand(Globals.prefs, getText())); copyButton.setGraphic(copyIcon); copyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); - copyButton.setStyle("-fx-border-width: 0"); - copyIcon.getStyleClass().add("checkmark-icon"); + copyIcon.getStyleClass().add("copy-icon"); checkmarkLayout.getChildren().setAll(copyButton); checkmarkLayout.setAlignment(Pos.TOP_CENTER); checkmarkLayout.setPrefWidth(28); From 8231f75049a0c16b168da813db97859508f6adb6 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:43:25 +0100 Subject: [PATCH 031/122] Implement a more complete HeaderView - Created ColumnConstraints that matches the field grid constraints, so when the window is resized grid and header resize equally --- .../newmergedialog/HeaderView.java | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java index ea7049cd857..359b11343c8 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java @@ -1,10 +1,65 @@ package org.jabref.gui.mergeentries.newmergedialog; -import javafx.scene.layout.HBox; +import javafx.geometry.Insets; +import javafx.scene.control.Control; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; -public class HeaderView extends HBox { - public HeaderView() { - setPrefHeight(50d); - setStyle("-fx-background-color: #878787"); +import org.jabref.gui.mergeentries.newmergedialog.cell.HeaderCell; + +/** + * I used a GridPane instead of a Hbox because Hbox allocates more space for cells + * with longer text, but I wanted all cells to have the same width + */ +public class HeaderView extends GridPane { + public static final int GRID_COLUMN_MIN_WIDTH = 250; + private final String leftHeader; + private final String rightHeader; + public HeaderView(String leftHeader, String rightHeader) { + this.leftHeader = leftHeader; + this.rightHeader = rightHeader; + setPrefHeight(Control.USE_COMPUTED_SIZE); + + setMaxHeight(Control.USE_PREF_SIZE); + setMinHeight(Control.USE_PREF_SIZE); + addRow( + 0, + new HeaderCell(""), + new HeaderCell(leftHeader), + new HeaderCell(rightHeader), + new HeaderCell("Merged Entry") + ); + + bindHeaderContainerWidthToFieldGridWidth(); + + ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); + fieldNameColumnConstraints.setHgrow(Priority.NEVER); + + ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + ColumnConstraints rightEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + ColumnConstraints mergedEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + + leftEntryColumnConstraints.setHgrow(Priority.ALWAYS); + rightEntryColumnConstraints.setHgrow(Priority.ALWAYS); + mergedEntryColumnConstraints.setHgrow(Priority.ALWAYS); + + getColumnConstraints().setAll( + fieldNameColumnConstraints, + leftEntryColumnConstraints, + rightEntryColumnConstraints, + mergedEntryColumnConstraints + ); + } + + /** + * The fields grid pane is contained within a scroll pane, thus it doesn't allocate the full available width. In + * fact, it uses the available width minus the size of the scrollbar which is 8. This leads to header columns being + * always larger than fields columns. This hack should solve this problem. + */ + private void bindHeaderContainerWidthToFieldGridWidth() { + setPadding(new Insets(0, 8, 0, 0)); + setStyle("-fx-background-color: #EFEFEF"); } } From 3575569becf25de60b802d1c54579cbbfed8e232 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:45:34 +0100 Subject: [PATCH 032/122] Pass left and right headers to HeaderView --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 3d3d204ce2c..f3565505977 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -29,7 +29,7 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHea viewModel = new ThreeWayMergeViewModel(leftEntry, rightEntry, leftHeader, rightHeader); mergeToolbox = new ThreeWayMergeToolbox(); - headerView = new HeaderView(); + headerView = new HeaderView(leftHeader, rightHeader); scrollPane = new ScrollPane(); scrollPane.setFitToWidth(true); scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); From 7aa387fc7ea696c15bbf50fdda3d261f7b48c366 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:49:28 +0100 Subject: [PATCH 033/122] Create merged entry --- .../gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java index 47912bf9f5b..3303b987501 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java @@ -37,6 +37,8 @@ public ThreeWayMergeViewModel(BibEntry leftEntry, BibEntry rightEntry, String le setLeftHeader(leftHeader); setRightHeader(rightHeader); + mergedEntry.set(new BibEntry()); + Set leftAndRightFieldsUnion = new HashSet<>(leftEntry.getFields()); leftAndRightFieldsUnion.addAll(rightEntry.getFields()); setAllFields(leftAndRightFieldsUnion); From 4a8f1e8e347cd59f24923c76e1bb351bf54714b4 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:50:18 +0100 Subject: [PATCH 034/122] Add the entry type field at the top of the fields list - Sorted fields by name - Removed internal fields --- .../newmergedialog/ThreeWayMergeViewModel.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java index 3303b987501..5314a725875 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeViewModel.java @@ -15,6 +15,8 @@ import org.jabref.gui.AbstractViewModel; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldFactory; +import org.jabref.model.entry.field.InternalField; class ThreeWayMergeViewModel extends AbstractViewModel { @@ -102,6 +104,12 @@ public int allFieldsSize() { private void setAllFields(Set fields) { allFields.clear(); allFields.addAll(fields); + // Don't show internal fields. See org.jabref.model.entry.field.InternalField + allFields.removeIf(FieldFactory::isInternalField); + allFields.sort(Comparator.comparing(Field::getName)); + + // Add the entry type field as the first field to display + allFields.add(0, InternalField.TYPE_HEADER); } } From 70522eb1b62ea0e87603f6f83a41b261e27c40d3 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:55:13 +0100 Subject: [PATCH 035/122] Display entry type and bind merged entry --- .../newmergedialog/FieldRowController.java | 36 +++++++++++++------ .../newmergedialog/ThreeWayMergeView.java | 35 ++++++++++++++---- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 6eebcaff257..9e9a077c3ea 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -3,7 +3,6 @@ import javafx.beans.property.ReadOnlyStringProperty; import javafx.scene.control.ToggleGroup; -import org.jabref.gui.mergeentries.newmergedialog.cell.AbstractCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; @@ -17,18 +16,37 @@ public class FieldRowController { private final ToggleGroup toggleGroup = new ToggleGroup(); - public FieldRowController(String fieldName, String leftValue, String rightValue, AbstractCell.BackgroundTone backgroundTone) { - fieldNameCell = new FieldNameCell(fieldName, backgroundTone); - leftValueCell = new FieldValueCell(leftValue, backgroundTone); - rightValueCell = new FieldValueCell(rightValue, backgroundTone); - mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, backgroundTone); + public FieldRowController(String fieldName, String leftValue, String rightValue, int rowIndex) { + fieldNameCell = new FieldNameCell(fieldName, rowIndex); + leftValueCell = new FieldValueCell(leftValue, rowIndex); + rightValueCell = new FieldValueCell(rightValue, rowIndex); + mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, rowIndex); toggleGroup.getToggles().addAll(leftValueCell, rightValueCell); toggleGroup.selectToggle(StringUtil.isNullOrEmpty(leftValue) ? rightValueCell : leftValueCell); toggleGroup.selectedToggleProperty().addListener(invalidated -> { - mergedValueCell.setText((String) toggleGroup.getSelectedToggle().getUserData()); + if (toggleGroup.getSelectedToggle() != null) { + mergedValueCell.setText((String) toggleGroup.getSelectedToggle().getUserData()); + } }); + mergedValueCell.textProperty().addListener((observable, old, mergedValue) -> { + if (!StringUtil.isNullOrEmpty(mergedValue)) { + if (mergedValue.equals(leftValue)) { + toggleGroup.selectToggle(leftValueCell); + } else if (mergedValue.equals(rightValue)) { + toggleGroup.selectToggle(rightValueCell); + } else { + // deselect all toggles because left and right values don't equal the merged value + toggleGroup.selectToggle(null); + } + } else { + // deselect all toggles because empty toggles cannot be selected + toggleGroup.selectToggle(null); + } + }); + + // empty toggles are disabled and cannot be selected if (StringUtil.isNullOrEmpty(leftValue)) { leftValueCell.setDisable(true); } else if (StringUtil.isNullOrEmpty(rightValue)) { @@ -65,8 +83,4 @@ public boolean hasEqualLeftAndRightValues() { !StringUtil.isNullOrEmpty(rightValueCell.getText()) && leftValueCell.getText().equals(rightValueCell.getText()); } - - public void deselectLeft() { - toggleGroup.getSelectedToggle().setSelected(false); - } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index f3565505977..268b98f3bd9 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -10,8 +10,8 @@ import org.jabref.gui.mergeentries.newmergedialog.toolbox.ThreeWayMergeToolbox; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; - -import static org.jabref.gui.mergeentries.newmergedialog.cell.AbstractCell.BackgroundTone; +import org.jabref.model.entry.field.InternalField; +import org.jabref.model.entry.types.EntryTypeFactory; public class ThreeWayMergeView extends VBox { public static final int GRID_COLUMN_MIN_WIDTH = 100; @@ -68,11 +68,30 @@ private void initializeMergeGridPane() { private void addFieldRow(int index) { Field field = viewModel.allFields().get(index); - String leftEntryValue = viewModel.getLeftEntry().getField(field).orElse(""); - String rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); - BackgroundTone backgroundTone = index % 2 == 0 ? BackgroundTone.DARK : BackgroundTone.LIGHT; - FieldRowController fieldRow = new FieldRowController(field.getDisplayName(), leftEntryValue, rightEntryValue, backgroundTone); + String leftEntryValue; + String rightEntryValue; + if (field.equals(InternalField.TYPE_HEADER)) { + leftEntryValue = viewModel.getLeftEntry().getType().getDisplayName(); + rightEntryValue = viewModel.getRightEntry().getType().getDisplayName(); + } else { + leftEntryValue = viewModel.getLeftEntry().getField(field).orElse(""); + rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); + } + + FieldRowController fieldRow = new FieldRowController(field.getDisplayName(), leftEntryValue, rightEntryValue, index); + fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { + if (field.equals(InternalField.TYPE_HEADER)) { + getMergedEntry().setType(EntryTypeFactory.parse(mergedValue)); + } else { + getMergedEntry().setField(field, mergedValue); + } + }); + if (field.equals(InternalField.TYPE_HEADER)) { + getMergedEntry().setType(EntryTypeFactory.parse(fieldRow.getMergedValue())); + } else { + getMergedEntry().setField(field, fieldRow.getMergedValue()); + } if (fieldRow.hasEqualLeftAndRightValues()) { mergeGridPane.add(fieldRow.getFieldNameCell(), 0, index, 1, 1); @@ -82,4 +101,8 @@ private void addFieldRow(int index) { mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); } } + + public BibEntry getMergedEntry() { + return viewModel.getMergedEntry(); + } } From 9a052772e8869bf8cc749e4bb2ab16b3e7038780 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:55:29 +0100 Subject: [PATCH 036/122] Minor changes --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 268b98f3bd9..a6322b34d8e 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -14,7 +14,7 @@ import org.jabref.model.entry.types.EntryTypeFactory; public class ThreeWayMergeView extends VBox { - public static final int GRID_COLUMN_MIN_WIDTH = 100; + public static final int GRID_COLUMN_MIN_WIDTH = 250; public static final String LEFT_DEFAULT_HEADER = "Left Entry"; public static final String RIGHT_DEFAULT_HEADER = "Right Entry"; private ThreeWayMergeToolbox mergeToolbox; @@ -39,7 +39,7 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHea scrollPane.setContent(mergeGridPane); getChildren().addAll(mergeToolbox, headerView, scrollPane); - this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.78); + this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.80); this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.95); } From d578b7ed6168de3b913abaa235f558a5171d1e32 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:55:51 +0100 Subject: [PATCH 037/122] Style CSS --- src/main/java/org/jabref/gui/Base.css | 8 +++++ src/main/java/org/jabref/gui/Dark.css | 3 ++ .../newmergedialog/ThreeWayMergeView.css | 35 +++++++++++++------ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 3ec5f717cf0..4c93cc0defa 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -2,6 +2,14 @@ .root { -jr-transparent-green: rgba(16, 172, 132, 0.12); + -jr-row-odd-background: -fx-control-inner-background; + -jr-row-even-background: -fx-control-inner-background-alt; + /* + On light theme, the text is hard to see when it's on top of the accent color. This is an alternative lighter accent color + for better text visibility. + */ + -jr-accent-alt: derive(-jr-accent, 10%); + /* The theme color and some derived colors from it are used for icons, tab-headers, marking of selected inputs and hover colors for the main menu. It generally defines the look of JabRef. The highlighting colors below should diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index e1de8084646..1ccb79df1e3 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -4,6 +4,9 @@ -jr-selected: -jr-accent; -jr-hover: #fff1; + -jr-row-odd-background: #272b38; + -jr-row-even-background: #212330; + -jr-accent-alt: -jr-accent; -jr-red: #b71c1f; -jr-light-red: #db1d2b; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index dbbe6a24b1c..db916d9010a 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -11,8 +11,8 @@ } .field-value:selected .selection-box { - -fx-background-color: derive(-jr-accent, 20%); - -fx-border-color: -jr-accent; + -fx-background-color: -jr-accent-alt; + -fx-border-color: -jr-accent-alt; } .field-value .selection-box { @@ -23,8 +23,8 @@ -fx-background-radius: 8; } -.field-value .label { - -fx-font-size: 13; +.field-value .styled-text-area .text{ + -fx-fill: -fx-text-background-color; } .field-value .text-area { @@ -32,21 +32,34 @@ } -.field-value .checkmark-icon { - -fx-icon-size: 16; - -fx-icon-color: -jr-gray-2; +.field-value .copy-icon { + -fx-icon-size: 15; + -fx-icon-color: -fx-text-background-color; -fx-opacity: 100%; } -.field-value:disabled .checkmark-icon { + +.field-value:disabled .copy-icon { -fx-opacity: 0%; } -.field-value:selected .checkmark-icon { - -fx-icon-size: 16; - -fx-icon-color: derive(-jr-accent, -30%);; +.field-value:selected .copy-icon { + -fx-icon-color: -fx-text-background-color; -fx-opacity: 100%; } +.field-cell:odd { + -fx-background-color: -jr-row-odd-background; +} + +.field-cell:even { + -fx-background-color: -jr-row-even-background; +} + +.merge-toolbox { + -fx-background-color: -jr-menu-background; +} + +/* Diff Highlighting */ .unchanged { } From 27dbcd160e7bfd8226030320b23f90c50f94ae63 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:56:08 +0100 Subject: [PATCH 038/122] Update ThreeWayMergeToolbox.fxml --- .../newmergedialog/toolbox/ThreeWayMergeToolbox.fxml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml index 8a729b85ecb..6829ee78b54 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml @@ -6,15 +6,16 @@ - + - + - - - + + + + From c9b87de680e33691f13d6288df602e58dc292b33 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 03:56:35 +0100 Subject: [PATCH 039/122] Remove MergeEntries from MergeEntriesDialog --- .../org/jabref/gui/mergeentries/MergeEntriesDialog.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index c75f89023a4..0a76be098a2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -9,12 +9,9 @@ import org.jabref.model.entry.BibEntry; public class MergeEntriesDialog extends BaseDialog { - - private final MergeEntries mergeEntries; private final ThreeWayMergeView threeWayMergeView; public MergeEntriesDialog(BibEntry one, BibEntry two) { - mergeEntries = new MergeEntries(one, two); threeWayMergeView = new ThreeWayMergeView(one, two); init(); @@ -31,7 +28,7 @@ private void init() { this.getDialogPane().getButtonTypes().setAll(ButtonType.CANCEL, replaceEntries); this.setResultConverter(buttonType -> { if (buttonType.equals(replaceEntries)) { - return null; + return threeWayMergeView.getMergedEntry(); } else { return null; } @@ -39,10 +36,10 @@ private void init() { } public void setLeftHeaderText(String leftHeaderText) { - mergeEntries.setLeftHeaderText(leftHeaderText); + // mergeEntries.setLeftHeaderText(leftHeaderText); } public void setRightHeaderText(String rightHeaderText) { - mergeEntries.setRightHeaderText(rightHeaderText); + // mergeEntries.setRightHeaderText(rightHeaderText); } } From 5e306a410f73a73082cb9c6fb270b90a97083f94 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:01:52 +0100 Subject: [PATCH 040/122] Create EmptyCell for optimization - When cell is empty there is no point of creating a label object --- .../mergeentries/newmergedialog/cell/EmptyCell.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java new file mode 100644 index 00000000000..e52ada18b13 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java @@ -0,0 +1,13 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +public class EmptyCell extends AbstractCell { + public EmptyCell(String styleClass, int rowIndex) { + super("", rowIndex); + getStyleClass().add(styleClass); + setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); + } + + public EmptyCell(String styleClass) { + this(styleClass, AbstractCell.NO_ROW_NUMBER); + } +} From 5c5c68a1bd504bb0885aaf8995d352c86c187b00 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:02:12 +0100 Subject: [PATCH 041/122] Update HeaderCell --- .../gui/mergeentries/newmergedialog/cell/HeaderCell.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java index d406a76cafe..1bc003200ca 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java @@ -1,6 +1,5 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; -import javafx.geometry.Insets; import javafx.scene.control.Label; public class HeaderCell extends AbstractCell { @@ -8,7 +7,7 @@ public class HeaderCell extends AbstractCell { private final Label label = new Label(); public HeaderCell(String text) { - super(text, 1); + super(text, AbstractCell.NO_ROW_NUMBER); initialize(); } @@ -16,12 +15,9 @@ private void initialize() { getStyleClass().add(DEFAULT_STYLE_CLASS); initializeLabel(); getChildren().add(label); - setStyle("-fx-border-width: 0 0 0.8 0; -fx-border-color: #424758"); } private void initializeLabel() { label.textProperty().bind(textProperty()); - label.setStyle("-fx-font-weight: bold; -fx-font-size: 13"); - label.setPadding(new Insets(1, 0, 1, 0)); } } From d14cb0f00162ee4d81ab633d14315df886483996 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:02:50 +0100 Subject: [PATCH 042/122] Rename HeaderView to ThreeWayMergeHeaderView --- .../newmergedialog/HeaderView.java | 65 ------------------- .../ThreeWayMergeHeaderView.java | 58 +++++++++++++++++ 2 files changed, 58 insertions(+), 65 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java deleted file mode 100644 index 359b11343c8..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/HeaderView.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.jabref.gui.mergeentries.newmergedialog; - -import javafx.geometry.Insets; -import javafx.scene.control.Control; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.RowConstraints; - -import org.jabref.gui.mergeentries.newmergedialog.cell.HeaderCell; - -/** - * I used a GridPane instead of a Hbox because Hbox allocates more space for cells - * with longer text, but I wanted all cells to have the same width - */ -public class HeaderView extends GridPane { - public static final int GRID_COLUMN_MIN_WIDTH = 250; - private final String leftHeader; - private final String rightHeader; - public HeaderView(String leftHeader, String rightHeader) { - this.leftHeader = leftHeader; - this.rightHeader = rightHeader; - setPrefHeight(Control.USE_COMPUTED_SIZE); - - setMaxHeight(Control.USE_PREF_SIZE); - setMinHeight(Control.USE_PREF_SIZE); - addRow( - 0, - new HeaderCell(""), - new HeaderCell(leftHeader), - new HeaderCell(rightHeader), - new HeaderCell("Merged Entry") - ); - - bindHeaderContainerWidthToFieldGridWidth(); - - ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); - fieldNameColumnConstraints.setHgrow(Priority.NEVER); - - ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); - ColumnConstraints rightEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); - ColumnConstraints mergedEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); - - leftEntryColumnConstraints.setHgrow(Priority.ALWAYS); - rightEntryColumnConstraints.setHgrow(Priority.ALWAYS); - mergedEntryColumnConstraints.setHgrow(Priority.ALWAYS); - - getColumnConstraints().setAll( - fieldNameColumnConstraints, - leftEntryColumnConstraints, - rightEntryColumnConstraints, - mergedEntryColumnConstraints - ); - } - - /** - * The fields grid pane is contained within a scroll pane, thus it doesn't allocate the full available width. In - * fact, it uses the available width minus the size of the scrollbar which is 8. This leads to header columns being - * always larger than fields columns. This hack should solve this problem. - */ - private void bindHeaderContainerWidthToFieldGridWidth() { - setPadding(new Insets(0, 8, 0, 0)); - setStyle("-fx-background-color: #EFEFEF"); - } -} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java new file mode 100644 index 00000000000..9299c5f2ef9 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java @@ -0,0 +1,58 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +import javafx.geometry.Insets; +import javafx.scene.control.Control; +import javafx.scene.layout.GridPane; + +import org.jabref.gui.mergeentries.newmergedialog.cell.EmptyCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.HeaderCell; + +/** + * I used a GridPane instead of a Hbox because Hbox allocates more space for cells + * with longer text, but I wanted all cells to have the same width + */ +public class ThreeWayMergeHeaderView extends GridPane { + public static final String DEFAULT_STYLE_CLASS = "merge-header"; + private final HeaderCell leftHeaderCell; + private final HeaderCell rightHeaderCell; + + private final HeaderCell mergedHeaderCell; + + public ThreeWayMergeHeaderView(String leftHeader, String rightHeader) { + getStyleClass().add(DEFAULT_STYLE_CLASS); + + this.leftHeaderCell = new HeaderCell(leftHeader); + this.rightHeaderCell = new HeaderCell(rightHeader); + this.mergedHeaderCell = new HeaderCell("Merged Entry"); + + addRow(0, + new EmptyCell(HeaderCell.DEFAULT_STYLE_CLASS), + leftHeaderCell, + rightHeaderCell, + mergedHeaderCell + ); + + setPrefHeight(Control.USE_COMPUTED_SIZE); + setMaxHeight(Control.USE_PREF_SIZE); + setMinHeight(Control.USE_PREF_SIZE); + + bindHeaderWidthToFieldGridWidth(); + } + + public void setLeftHeader(String leftHeader) { + leftHeaderCell.setText(leftHeader); + } + + public void setRightHeader(String rightHeader) { + rightHeaderCell.setText(rightHeader); + } + + /** + * The fields grid pane is contained within a scroll pane, thus it doesn't allocate the full available width. In + * fact, it uses the available width minus the size of the scrollbar which is 8. This leads to header columns being + * always larger than fields columns. This hack should fix it. + */ + private void bindHeaderWidthToFieldGridWidth() { + setPadding(new Insets(0, 8, 0, 0)); + } +} From 23be621d209662fed9c2e657fd1dc77dd7229366 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:04:13 +0100 Subject: [PATCH 043/122] Add buttons to accept all left or right entry changes to ThreeWayMergeToolbar --- .../java/org/jabref/gui/icon/IconTheme.java | 6 +- .../toolbar/ThreeWayMergeToolbar.fxml | 48 +++++++++++ .../ThreeWayMergeToolbar.java} | 79 +++++++++++-------- .../toolbox/ThreeWayMergeToolbox.fxml | 28 ------- 4 files changed, 98 insertions(+), 63 deletions(-) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml rename src/main/java/org/jabref/gui/mergeentries/newmergedialog/{toolbox/ThreeWayMergeToolbox.java => toolbar/ThreeWayMergeToolbar.java} (65%) delete mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index cbaa6fac7d8..8ef3ed5d490 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -339,7 +339,11 @@ public enum JabRefIcons implements JabRefIcon { KEEP_SEARCH_STRING(MaterialDesignE.EARTH), KEEP_ON_TOP(MaterialDesignP.PIN), KEEP_ON_TOP_OFF(MaterialDesignP.PIN_OFF_OUTLINE), - OPEN_GLOBAL_SEARCH(MaterialDesignO.OPEN_IN_NEW); + OPEN_GLOBAL_SEARCH(MaterialDesignO.OPEN_IN_NEW), + + ACCEPT_LEFT(MaterialDesignS.SUBDIRECTORY_ARROW_LEFT), + + ACCEPT_RIGHT(MaterialDesignS.SUBDIRECTORY_ARROW_RIGHT); private final JabRefIcon icon; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml new file mode 100644 index 00000000000..39df997c47b --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java similarity index 65% rename from src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java rename to src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java index 7f6b50beb88..0703f49f7c2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java @@ -1,14 +1,18 @@ -package org.jabref.gui.mergeentries.newmergedialog.toolbox; +package org.jabref.gui.mergeentries.newmergedialog.toolbar; import java.util.Arrays; +import java.util.function.Consumer; +import javafx.beans.binding.BooleanExpression; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.RadioButton; import javafx.scene.control.ToggleGroup; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.util.StringConverter; @@ -16,15 +20,15 @@ import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBinding; -public class ThreeWayMergeToolbox extends AnchorPane { +public class ThreeWayMergeToolbar extends AnchorPane { @FXML - private RadioButton compareCharactersRadioButtons; + private RadioButton highlightCharactersRadioButtons; @FXML - private RadioButton compareWordsRadioButton; + private RadioButton highlightWordsRadioButton; @FXML - private ToggleGroup diffCompareMethodToggleGroup; + private ToggleGroup diffHighlightModeToggleGroup; @FXML private ComboBox diffViewComboBox; @@ -32,10 +36,16 @@ public class ThreeWayMergeToolbox extends AnchorPane { @FXML private ComboBox plainTextOrDiffComboBox; - private final ObjectProperty diffCompareMethod = new SimpleObjectProperty<>(); + @FXML + private Button selectLeftEntryValuesButton; + + @FXML + private Button selectRightEntryValuesButton; + + private final ObjectProperty diffHighlightMode = new SimpleObjectProperty<>(); private EasyBinding showDiff; - public ThreeWayMergeToolbox() { + public ThreeWayMergeToolbar() { ViewLoader.view(this) .root(this) .load(); @@ -74,33 +84,18 @@ public DiffView fromString(String string) { } }); - compareWordsRadioButton.disableProperty().bind(notShowDiffProperty()); - compareCharactersRadioButtons.disableProperty().bind(notShowDiffProperty()); + highlightWordsRadioButton.disableProperty().bind(notShowDiffProperty()); + highlightCharactersRadioButtons.disableProperty().bind(notShowDiffProperty()); - diffCompareMethodToggleGroup.selectedToggleProperty().addListener((observable -> { - if (diffCompareMethodToggleGroup.getSelectedToggle().equals(compareCharactersRadioButtons)) { - diffCompareMethod.set(DiffCompareMethod.CHARS); + diffHighlightModeToggleGroup.selectedToggleProperty().addListener((observable -> { + if (diffHighlightModeToggleGroup.getSelectedToggle().equals(highlightCharactersRadioButtons)) { + diffHighlightMode.set(DiffHighlightMode.CHARS); } else { - diffCompareMethod.set(DiffCompareMethod.WORDS); + diffHighlightMode.set(DiffHighlightMode.WORDS); } })); - diffCompareMethodToggleGroup.selectToggle(compareWordsRadioButton); - } - - private void initializeDiffViewComboBox() { - diffViewComboBox.getSelectionModel().select(DiffView.UNIFIED); - diffViewComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(DiffView diffView) { - return diffView.getValue(); - } - - @Override - public DiffView fromString(String string) { - return DiffView.fromString(string); - } - }); + diffHighlightModeToggleGroup.selectToggle(highlightWordsRadioButton); } public ReadOnlyObjectProperty diffViewProperty() { @@ -115,6 +110,14 @@ public EasyBinding showDiffProperty() { return showDiff; } + /** + * Convince method used to disable diff related views when diff is not selected. + * + *

+ * This method is required because {@link EasyBinding} class doesn't have a method to invert a boolean property, + * like {@link BooleanExpression#not()} + *

+ * */ public EasyBinding notShowDiffProperty() { return showDiffProperty().map(showDiff -> !showDiff); } @@ -123,12 +126,20 @@ public Boolean isShowDiffEnabled() { return showDiffProperty().get(); } - public ObjectProperty diffCompareMethodProperty() { - return diffCompareMethod; + public ObjectProperty diffHighlightModeProperty() { + return diffHighlightMode; + } + + public DiffHighlightMode getDiffHighlightMode() { + return diffHighlightModeProperty().get(); + } + + public void setOnSelectLeftEntryValuesButtonClicked(Consumer onClick) { + selectLeftEntryValuesButton.setOnMouseClicked(onClick::accept); } - public DiffCompareMethod getDiffCompareMethod() { - return diffCompareMethodProperty().get(); + public void setOnSelectRightEntryValuesButtonClicked(Consumer onClick) { + selectRightEntryValuesButton.setOnMouseClicked(onClick::accept); } public enum PlainTextOrDiff { @@ -173,7 +184,7 @@ public static DiffView fromString(String str) { } } - public enum DiffCompareMethod { + public enum DiffHighlightMode { WORDS, CHARS } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml deleted file mode 100644 index 6829ee78b54..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbox/ThreeWayMergeToolbox.fxml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 0fe1f1ba107b5cb116485ba4a09f8546cf104098 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:05:07 +0100 Subject: [PATCH 044/122] Disable right field cell when it's not visible --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index a6322b34d8e..cee05b99050 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -97,6 +97,7 @@ private void addFieldRow(int index) { mergeGridPane.add(fieldRow.getFieldNameCell(), 0, index, 1, 1); mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index, 2, 1); mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index, 1, 1); + fieldRow.getRightValueCell().setDisable(true); } else { mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); } From ae10410766eb90e89287b537e6fbf04b4f5a78b3 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:06:15 +0100 Subject: [PATCH 045/122] Listen for select all left/right toolbar buttons --- .../newmergedialog/FieldRowController.java | 22 +++++++ .../newmergedialog/ThreeWayMergeView.java | 62 ++++++++++++++----- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 9e9a077c3ea..904ca57c390 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -54,6 +54,28 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, } } + /** + * @return True if left value was selected, False otherwise + */ + public boolean selectLeftValue() { + if (!leftValueCell.isDisabled()) { + toggleGroup.selectToggle(leftValueCell); + return true; + } + return false; + } + + /** + * @return True if left value was selected, False otherwise + */ + public boolean selectRightValue() { + if (!rightValueCell.isDisabled()) { + toggleGroup.selectToggle(rightValueCell); + return true; + } + return false; + } + public String getMergedValue() { return mergedValueProperty().getValue(); } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index cee05b99050..4ec81f6d6f2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -1,5 +1,8 @@ package org.jabref.gui.mergeentries.newmergedialog; +import java.util.ArrayList; +import java.util.List; + import javafx.scene.control.ScrollPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; @@ -7,58 +10,81 @@ import javafx.scene.layout.VBox; import javafx.stage.Screen; -import org.jabref.gui.mergeentries.newmergedialog.toolbox.ThreeWayMergeToolbox; +import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.InternalField; import org.jabref.model.entry.types.EntryTypeFactory; public class ThreeWayMergeView extends VBox { + public static final int GRID_COLUMN_MIN_WIDTH = 250; public static final String LEFT_DEFAULT_HEADER = "Left Entry"; public static final String RIGHT_DEFAULT_HEADER = "Right Entry"; - private ThreeWayMergeToolbox mergeToolbox; - private HeaderView headerView; + + private final ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); + private final ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + private final ColumnConstraints rightEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + private final ColumnConstraints mergedEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + private final ThreeWayMergeToolbar toolbar; + private final ThreeWayMergeHeaderView headerView; private final ScrollPane scrollPane; private final GridPane mergeGridPane; private final ThreeWayMergeViewModel viewModel; + private final List fieldRowControllerList = new ArrayList<>(); public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { getStylesheets().add(ThreeWayMergeView.class.getResource("ThreeWayMergeView.css").toExternalForm()); viewModel = new ThreeWayMergeViewModel(leftEntry, rightEntry, leftHeader, rightHeader); - mergeToolbox = new ThreeWayMergeToolbox(); - headerView = new HeaderView(leftHeader, rightHeader); - scrollPane = new ScrollPane(); - scrollPane.setFitToWidth(true); - scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); mergeGridPane = new GridPane(); - initializeMergeGridPane(); + scrollPane = new ScrollPane(); + headerView = new ThreeWayMergeHeaderView(leftHeader, rightHeader); + toolbar = new ThreeWayMergeToolbar(); - scrollPane.setContent(mergeGridPane); - getChildren().addAll(mergeToolbox, headerView, scrollPane); + initializeColumnConstraints(); + initializeMergeGridPane(); + initializeScrollPane(); + initializeHeaderView(); + initializeToolbar(); this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.80); this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.95); + + getChildren().addAll(toolbar, headerView, scrollPane); } public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry) { this(leftEntry, rightEntry, LEFT_DEFAULT_HEADER, RIGHT_DEFAULT_HEADER); } - private void initializeMergeGridPane() { - ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); - fieldNameColumnConstraints.setHgrow(Priority.NEVER); + private void initializeToolbar() { + toolbar.setOnSelectLeftEntryValuesButtonClicked(e -> fieldRowControllerList.forEach(FieldRowController::selectLeftValue)); + toolbar.setOnSelectRightEntryValuesButtonClicked(e -> fieldRowControllerList.forEach(FieldRowController::selectRightValue)); + } + + private void initializeHeaderView() { + headerView.getColumnConstraints().addAll(fieldNameColumnConstraints, + leftEntryColumnConstraints, + rightEntryColumnConstraints, + mergedEntryColumnConstraints); + } - ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); - ColumnConstraints rightEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); - ColumnConstraints mergedEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); + private void initializeScrollPane() { + scrollPane.setFitToWidth(true); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setContent(mergeGridPane); + } + private void initializeColumnConstraints() { + fieldNameColumnConstraints.setHgrow(Priority.NEVER); leftEntryColumnConstraints.setHgrow(Priority.ALWAYS); rightEntryColumnConstraints.setHgrow(Priority.ALWAYS); mergedEntryColumnConstraints.setHgrow(Priority.ALWAYS); + } + private void initializeMergeGridPane() { mergeGridPane.getColumnConstraints().addAll(fieldNameColumnConstraints, leftEntryColumnConstraints, rightEntryColumnConstraints, mergedEntryColumnConstraints); for (int fieldIndex = 0; fieldIndex < viewModel.allFieldsSize(); fieldIndex++) { @@ -80,6 +106,8 @@ private void addFieldRow(int index) { } FieldRowController fieldRow = new FieldRowController(field.getDisplayName(), leftEntryValue, rightEntryValue, index); + fieldRowControllerList.add(fieldRow); + fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { getMergedEntry().setType(EntryTypeFactory.parse(mergedValue)); From d26a6bb7d06f91bcff8177b389ae16973d907127 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:07:45 +0100 Subject: [PATCH 046/122] Style stuff --- src/main/java/org/jabref/gui/Base.css | 31 +++++++++++-- src/main/java/org/jabref/gui/Dark.css | 25 +++++++++++ .../newmergedialog/ThreeWayMergeView.css | 44 +++++++------------ 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 4c93cc0defa..caf46e64a27 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -2,13 +2,13 @@ .root { -jr-transparent-green: rgba(16, 172, 132, 0.12); - -jr-row-odd-background: -fx-control-inner-background; - -jr-row-even-background: -fx-control-inner-background-alt; + -jr-row-odd-background: -fx-control-inner-background-alt; + -jr-row-even-background: -fx-control-inner-background; /* On light theme, the text is hard to see when it's on top of the accent color. This is an alternative lighter accent color for better text visibility. */ - -jr-accent-alt: derive(-jr-accent, 10%); + -jr-accent-alt: derive(-jr-accent, 15%); /* The theme color and some derived colors from it are used for icons, tab-headers, marking of selected inputs and @@ -615,6 +615,31 @@ TextFlow > .tooltip-text-monospaced { -fx-background-insets: 0; } +.field-value .copy-icon { + -fx-icon-size: 15; + -fx-icon-color: -fx-text-background-color; + -fx-blend-mode: multiply ; + -fx-opacity: 69%; +} + +.field-value:disabled .copy-icon { + -fx-opacity: 0%; +} + +.field-value:selected .copy-icon { + +} + +.header-cell { + -fx-border-width: 0 0 1 0; + -fx-border-color: -jr-gray-1; + -fx-background-color: -jr-row-even-background; +} + +.merge-header { + -fx-background-color: -jr-row-even-background; +} + .table-view .groupColumnBackground { -fx-stroke: -jr-gray-2; } diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 1ccb79df1e3..08e7d34764c 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -81,6 +81,31 @@ .tree-table-row-cell:hover { -fx-background-color: -jr-hover; } +.field-value .copy-icon { + -fx-icon-size: 15; + -fx-icon-color: -fx-text-background-color; + -fx-blend-mode: inherit; + -fx-opacity: 100%; +} + +.field-value:disabled .copy-icon { + -fx-opacity: 0%; +} + +.field-value:selected .copy-icon { + -fx-icon-color: -fx-text-background-color; + -fx-opacity: 100%; +} + +.header-cell { + -fx-border-width: 0 0 1 0; + -fx-border-color: -fx-outer-border; + -fx-background-color: -jr-row-odd-background; +} + +.merge-header { + -fx-background-color: -jr-row-odd-background; +} .table-view .groupColumnBackground { -fx-stroke: -jr-gray-3; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index db916d9010a..815114bc7ae 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -1,13 +1,18 @@ .merged-field { - -fx-border-color: #dfdfdf; - -fx-border-width: 0 0 0 1.5; +/* -fx-border-color: #dfdfdf; + -fx-border-width: 0 0 0 1.5;*/ } .merged-field .styled-text-area { - -fx-background-radius: 3; - -fx-border-radius: 3; - -fx-border-color: #dfdfdf; - -fx-background-color: white; + -fx-background-color: -fx-outer-border, -fx-control-inner-background; + -fx-background-insets: 0, 1; + -fx-prompt-text-fill: -fx-mid-text-color; +} +.merged-field .styled-text-area:focused { + -fx-highlight-fill: derive(-jr-accent, 20%); + -fx-background-color: -jr-accent, -fx-control-inner-background; + -fx-background-insets: 0, 2; + -fx-highlight-text-fill: -fx-text-inner-color; } .field-value:selected .selection-box { @@ -23,30 +28,10 @@ -fx-background-radius: 8; } -.field-value .styled-text-area .text{ +.styled-text-area .text{ -fx-fill: -fx-text-background-color; } -.field-value .text-area { - -fx-font-size: 13; -} - - -.field-value .copy-icon { - -fx-icon-size: 15; - -fx-icon-color: -fx-text-background-color; - -fx-opacity: 100%; -} - -.field-value:disabled .copy-icon { - -fx-opacity: 0%; -} - -.field-value:selected .copy-icon { - -fx-icon-color: -fx-text-background-color; - -fx-opacity: 100%; -} - .field-cell:odd { -fx-background-color: -jr-row-odd-background; } @@ -59,6 +44,11 @@ -fx-background-color: -jr-menu-background; } +.header-cell .label{ + -fx-font-weight: bold; + -fx-padding: 1, 0, 1, 0; +} + /* Diff Highlighting */ .unchanged { From d89d5587fef3a84c882462057bcd43ef5965e1fd Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:31:58 +0100 Subject: [PATCH 047/122] Add DiffHighlighter - Implemented UnifiedDiffHighlighter --- .../diffhighlighter/DiffHighlighter.java | 69 +++++++++++ .../diffhighlighter/SplitDiffHighlighter.java | 24 ++++ .../UnifiedDiffHighlighter.java | 117 ++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java new file mode 100644 index 00000000000..e92a85e0a96 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java @@ -0,0 +1,69 @@ +package org.jabref.gui.mergeentries.newmergedialog.diffhighlighter; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.fxmisc.richtext.StyleClassedTextArea; + +public abstract sealed class DiffHighlighter permits SplitDiffHighlighter, UnifiedDiffHighlighter { + protected final StyleClassedTextArea sourceTextview; + protected final StyleClassedTextArea targetTextview; + + protected DiffMethod diffMethod; + + public DiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview, DiffMethod diffMethod) { + Objects.requireNonNull(sourceTextview, "source text view MUST NOT be null."); + Objects.requireNonNull(targetTextview, "target text view MUST NOT be null."); + + this.sourceTextview = sourceTextview; + this.targetTextview = targetTextview; + this.diffMethod = diffMethod; + } + + abstract void highlight(); + + protected List splitString(String str) { + return Arrays.asList(str.split(diffMethod.separator())); + } + + private void setDiffMethod(DiffMethod diffMethod) { + this.diffMethod = diffMethod; + } + + public DiffMethod getDiffMethod() { + return diffMethod; + } + + public String getSeparator() { + return diffMethod.separator(); + } + + enum DiffMethod { + WORDS(" "), CHARS(""); + + private final String separator; + + DiffMethod(String separator) { + this.separator = separator; + } + + public String separator() { + return separator; + } + } + + protected String join(List stringList) { + return String.join(" ", stringList); + } + + enum ChangeType { + ADDITION, DELETION, CHANGE_DELETION + } + + record Change( + int position, + int spanSize, + ChangeType type) { + } +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java new file mode 100644 index 00000000000..c24868e9a37 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java @@ -0,0 +1,24 @@ +package org.jabref.gui.mergeentries.newmergedialog.diffhighlighter; + +import com.github.difflib.patch.DeltaType; +import org.fxmisc.richtext.StyleClassedTextArea; + +/** + * A diff highlighter in which changes of type {@link DeltaType#CHANGE} are split between source and target + * text view. They are represented by an addition in the target text view and deletion in the source text view. + * Normal addition and deletion are kept as they are. + */ +public final class SplitDiffHighlighter extends DiffHighlighter { + + public SplitDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview, DiffMethod diffMethod) { + super(sourceTextview, targetTextview, diffMethod); + } + + public SplitDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview) { + this(sourceTextview, targetTextview, DiffMethod.WORDS); + } + + @Override + public void highlight() { + } +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java new file mode 100644 index 00000000000..216ba52369f --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java @@ -0,0 +1,117 @@ +package org.jabref.gui.mergeentries.newmergedialog.diffhighlighter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import com.github.difflib.DiffUtils; +import com.github.difflib.patch.AbstractDelta; +import com.github.difflib.patch.DeltaType; +import org.fxmisc.richtext.StyleClassedTextArea; + +/** + * A diff highlighter in which differences of type {@link DeltaType#CHANGE} are unified and represented by an insertion + * and deletion in the target text view. Normal addition and deletion are kept as they are. + */ +public final class UnifiedDiffHighlighter extends DiffHighlighter { + + public UnifiedDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview, DiffMethod diffMethod) { + super(sourceTextview, targetTextview, diffMethod); + } + + public UnifiedDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview) { + this(sourceTextview, targetTextview, DiffMethod.WORDS); + } + + @SuppressWarnings({"checkstyle:RegexpMultiline", "checkstyle:EmptyBlock"}) + @Override + public void highlight() { + String sourceContent = sourceTextview.getText(); + String targetContent = targetTextview.getText(); + if (sourceContent.equals(targetContent)) { + return; + } + + List sourceWords = splitString(sourceContent); + List targetWords = splitString(targetContent); + List unifiedWords = new ArrayList<>(targetWords); + + List> deltaList = DiffUtils.diff(sourceWords, targetWords).getDeltas(); + + List changeList = new ArrayList<>(); + + int deletionCount = 0; + for (AbstractDelta delta : deltaList) { + switch (delta.getType()) { + case CHANGE -> { + int changePosition = delta.getTarget().getPosition(); + int deletionPoint = changePosition + deletionCount; + int insertionPoint = deletionPoint + 1; + List deltaSourceWords = delta.getSource().getLines(); + List deltaTargetWords = delta.getTarget().getLines(); + + unifiedWords.add(deletionPoint, join(deltaSourceWords)); + + changeList.add(new Change(deletionPoint, 1, ChangeType.CHANGE_DELETION)); + changeList.add(new Change(insertionPoint, deltaTargetWords.size(), ChangeType.ADDITION)); + deletionCount++; + } + case DELETE -> { + int deletionPoint = delta.getTarget().getPosition() + deletionCount; + unifiedWords.add(deletionPoint, join(delta.getSource().getLines())); + + changeList.add(new Change(deletionPoint, 1, ChangeType.DELETION)); + deletionCount++; + } + case INSERT -> { + int insertionPoint = delta.getTarget().getPosition() + deletionCount; + changeList.add(new Change(insertionPoint, delta.getTarget().getLines().size(), ChangeType.ADDITION)); + } + } + } + targetTextview.clear(); + + boolean changeInProgress = false; + for (int position = 0; position < unifiedWords.size(); position++) { + String word = unifiedWords.get(position); + Optional changeAtPosition = findChange(position, changeList); + if (changeAtPosition.isEmpty()) { + appendToTextArea(targetTextview, getSeparator() + word, "unchanged"); + } else { + Change change = changeAtPosition.get(); + List changeWords = unifiedWords.subList(change.position(), change.position() + change.spanSize()); + + if (change.type() == ChangeType.DELETION) { + appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion"); + } else if (change.type() == ChangeType.ADDITION) { + if (changeInProgress) { + appendToTextArea(targetTextview, join(changeWords), "addition"); + changeInProgress = false; + } else { + appendToTextArea(targetTextview, getSeparator() + join(changeWords), "addition"); + } + } else if (change.type() == ChangeType.CHANGE_DELETION) { + appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion"); + changeInProgress = true; + } + position = position + changeWords.size() - 1; + } + } + } + + private void appendToTextArea(StyleClassedTextArea textArea, String text, String styleClass) { + if (text.isEmpty()) { + return; + } + // Append separator without styling it + if (text.startsWith(getSeparator())) { + textArea.appendText(getSeparator()); + } + int separatorIndex = text.indexOf(getSeparator()); + textArea.append(text.substring(separatorIndex != -1 ? separatorIndex + 1 : 0), styleClass); + } + + private Optional findChange(int position, List changeList) { + return changeList.stream().filter(change -> change.position() == position).findAny(); + } +} From 9c5e623eb3467f7009fd08492a88ce56df401e69 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:32:28 +0100 Subject: [PATCH 048/122] Create DiffHighlighter.css --- .../newmergedialog/diffhighlighter/DiffHighlighter.css | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.css diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.css new file mode 100644 index 00000000000..e69de29bb2d From 9e975985a9c3e8d7ee6fa9fcd14e4bc97994cdd2 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:33:23 +0100 Subject: [PATCH 049/122] Add left padding to HeaderCell --- .../jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java index 1bc003200ca..5a7c6a9ff71 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java @@ -1,5 +1,6 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; +import javafx.geometry.Insets; import javafx.scene.control.Label; public class HeaderCell extends AbstractCell { @@ -19,5 +20,6 @@ private void initialize() { private void initializeLabel() { label.textProperty().bind(textProperty()); + label.setPadding(new Insets(getPadding().getTop(), getPadding().getRight(), getPadding().getBottom(), 16)); } } From 6bad42874283ed8a4ccef075fb87764a880ead93 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:34:45 +0100 Subject: [PATCH 050/122] Move diff highlighting styles to Dark.css - Just for testing --- src/main/java/org/jabref/gui/Dark.css | 13 +++++++++++++ .../newmergedialog/ThreeWayMergeView.css | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 08e7d34764c..2a5141d545a 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -60,6 +60,19 @@ -jr-drag-target-hover: -jr-accent; } +.unchanged { + +} + +.addition { + -rtfx-background-color: #0B1935; +} + +.deletion { + -rtfx-background-color: #4E0A0D; + +} + #previewBody { background-color: #272b38; /* -fx-control-inner-background*/ color: #7d8591; /* -fx-mid-text-color*/ diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index 815114bc7ae..5f4731c9f27 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -48,16 +48,3 @@ -fx-font-weight: bold; -fx-padding: 1, 0, 1, 0; } - -/* Diff Highlighting */ -.unchanged { - -} - -.addition { - -rtfx-background-color: rgba(35, 225, 96, 0.26); -} - -.deletion { - -rtfx-background-color: rgba(248, 62, 68, 0.26); -} From 9cc0d188c4882a1b24bd9713eba3751e77c1d962 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:35:53 +0100 Subject: [PATCH 051/122] Show differences between left and right values using a unified diff view - Just for testing --- .../gui/mergeentries/newmergedialog/FieldRowController.java | 6 ++++++ .../mergeentries/newmergedialog/cell/FieldValueCell.java | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 904ca57c390..f145ec1132d 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -6,6 +6,7 @@ import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.UnifiedDiffHighlighter; import org.jabref.model.strings.StringUtil; public class FieldRowController { @@ -52,6 +53,11 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, } else if (StringUtil.isNullOrEmpty(rightValue)) { rightValueCell.setDisable(true); } + + // Debugging + if (!leftValueCell.isDisabled() && !rightValueCell.isDisabled()) { + new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), rightValueCell.getStyleClassedLabel()).highlight(); + } } /** diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 8945cf688e5..440cf9e51a0 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -191,4 +191,8 @@ public void setUserData(Object value) { public Object getUserData() { return super.getText(); } + + public StyleClassedTextArea getStyleClassedLabel() { + return label; + } } From c37c3c1a4ddab1834fd4a64b4f8f56d9867e2211 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:36:09 +0100 Subject: [PATCH 052/122] Change MergeEntriesDialog stage style --- .../java/org/jabref/gui/mergeentries/MergeEntriesDialog.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index 0a76be098a2..b70a2dc915e 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -2,6 +2,7 @@ import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; +import javafx.stage.StageStyle; import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView; import org.jabref.gui.util.BaseDialog; @@ -22,6 +23,7 @@ public MergeEntriesDialog(BibEntry one, BibEntry two) { */ private void init() { this.getDialogPane().setContent(threeWayMergeView); + this.initStyle(StageStyle.UTILITY); // Create buttons ButtonType replaceEntries = new ButtonType(Localization.lang("Merge entries"), ButtonBar.ButtonData.OK_DONE); From 39ee372dbd1e27ce2a2e17299d67b497c1f55d4e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:38:40 +0100 Subject: [PATCH 053/122] Remove BackgroundTone from AbstractCell --- .../newmergedialog/cell/AbstractCell.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java index 10b13bd5b71..ffb1f536986 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/AbstractCell.java @@ -7,7 +7,6 @@ import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.scene.layout.HBox; -import javafx.scene.paint.Color; /** * @@ -80,17 +79,4 @@ public StringProperty textProperty() { public void setText(String text) { textProperty().set(text); } - - public enum BackgroundTone { - LIGHT(Color.web("#FEFEFE")), DARK(Color.web("#EFEFEF")); - private final Color color; - - BackgroundTone(Color color) { - this.color = color; - } - - public Color color() { - return color; - } - } } From 2ed006256d0d23fab497b8208cef970bad0aa701 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:39:09 +0100 Subject: [PATCH 054/122] Move CopyFieldValueCommand --- .../newmergedialog/{cell => }/CopyFieldValueCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/org/jabref/gui/mergeentries/newmergedialog/{cell => }/CopyFieldValueCommand.java (92%) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/CopyFieldValueCommand.java similarity index 92% rename from src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java rename to src/main/java/org/jabref/gui/mergeentries/newmergedialog/CopyFieldValueCommand.java index e18a831933b..a3ef9024215 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/CopyFieldValueCommand.java @@ -1,4 +1,4 @@ -package org.jabref.gui.mergeentries.newmergedialog.cell; +package org.jabref.gui.mergeentries.newmergedialog; import java.util.Objects; From 3ea5cd193e655d4f8e14e4ba3c09e083c44a1569 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:39:37 +0100 Subject: [PATCH 055/122] Make DiffMethod public --- .../newmergedialog/diffhighlighter/DiffHighlighter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java index e92a85e0a96..17149e368d6 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.java @@ -39,7 +39,7 @@ public String getSeparator() { return diffMethod.separator(); } - enum DiffMethod { + public enum DiffMethod { WORDS(" "), CHARS(""); private final String separator; @@ -54,7 +54,7 @@ public String separator() { } protected String join(List stringList) { - return String.join(" ", stringList); + return String.join(getSeparator(), stringList); } enum ChangeType { From 758c13dc5fab5eca2a325352415ff44c1b9bb984 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:40:54 +0100 Subject: [PATCH 056/122] Move CopyFieldValueCommand --- .../gui/mergeentries/newmergedialog/cell/FieldValueCell.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 440cf9e51a0..649d95e20d9 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -24,6 +24,7 @@ import org.jabref.gui.Globals; import org.jabref.gui.actions.ActionFactory; +import org.jabref.gui.mergeentries.newmergedialog.CopyFieldValueCommand; import org.jabref.logic.l10n.Localization; import org.fxmisc.flowless.VirtualizedScrollPane; @@ -107,7 +108,7 @@ private void initializeLabel() { label.prefHeightProperty().bind(label.totalHeightEstimateProperty().orElseConst(-1d)); - // Fix text area consuming scroll events before they rich the outer scrollable + // Fix text area consuming scroll events before they reach the outer scrollable label.addEventFilter(ScrollEvent.SCROLL, e -> { e.consume(); FieldValueCell.this.fireEvent(e.copyFor(e.getSource(), FieldValueCell.this)); From d815bbe31e2968659c9ecfbf3f90d64a2b8210a1 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:41:34 +0100 Subject: [PATCH 057/122] Implement SplitDiffHighlighter --- src/main/java/org/jabref/gui/Base.css | 13 +++ src/main/java/org/jabref/gui/Dark.css | 5 +- .../diffhighlighter/SplitDiffHighlighter.java | 99 +++++++++++++++++++ 3 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index caf46e64a27..bc2c914258e 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -253,6 +253,19 @@ -jr-header-height: 3em; } +.unchanged { + -rtfx-background-color:#0000; +} + +.addition { + -rtfx-background-color: rgba(29, 209, 161, 0.5); +} + +.deletion { + -rtfx-background-color: rgba(255, 107, 107, 0.55); +} + + #frame { -fx-background-color: -jr-background-alt; } diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 2a5141d545a..63bd21c0a99 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -65,12 +65,11 @@ } .addition { - -rtfx-background-color: #0B1935; + -rtfx-background-color: -jr-green; } .deletion { - -rtfx-background-color: #4E0A0D; - + -rtfx-background-color: -jr-red; } #previewBody { diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java index c24868e9a37..5e3ba6274c5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java @@ -1,5 +1,11 @@ package org.jabref.gui.mergeentries.newmergedialog.diffhighlighter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import com.github.difflib.DiffUtils; +import com.github.difflib.patch.AbstractDelta; import com.github.difflib.patch.DeltaType; import org.fxmisc.richtext.StyleClassedTextArea; @@ -20,5 +26,98 @@ public SplitDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTex @Override public void highlight() { + String sourceContent = sourceTextview.getText(); + String targetContent = targetTextview.getText(); + if (sourceContent.equals(targetContent)) { + return; + } + + List sourceWords = splitString(sourceContent); + List targetWords = splitString(targetContent); + List unifiedWords = new ArrayList<>(targetWords); + + List> deltaList = DiffUtils.diff(sourceWords, targetWords).getDeltas(); + + List changeList = new ArrayList<>(); + + int deletionCount = 0; + for (AbstractDelta delta : deltaList) { + switch (delta.getType()) { + case CHANGE -> { + int changePosition = delta.getTarget().getPosition(); + int deletionPoint = changePosition + deletionCount; + int insertionPoint = deletionPoint + 1; + List deltaSourceWords = delta.getSource().getLines(); + List deltaTargetWords = delta.getTarget().getLines(); + + unifiedWords.add(deletionPoint, join(deltaSourceWords)); + + changeList.add(new Change(deletionPoint, 1, ChangeType.CHANGE_DELETION)); + changeList.add(new Change(insertionPoint, deltaTargetWords.size(), ChangeType.ADDITION)); + deletionCount++; + } + case DELETE -> { + int deletionPoint = delta.getTarget().getPosition() + deletionCount; + unifiedWords.add(deletionPoint, join(delta.getSource().getLines())); + + changeList.add(new Change(deletionPoint, 1, ChangeType.DELETION)); + deletionCount++; + } + case INSERT -> { + int insertionPoint = delta.getTarget().getPosition() + deletionCount; + changeList.add(new Change(insertionPoint, delta.getTarget().getLines().size(), ChangeType.ADDITION)); + } + } + } + sourceTextview.clear(); + targetTextview.clear(); + + boolean changeInProgress = false; + for (int position = 0; position < unifiedWords.size(); position++) { + String word = unifiedWords.get(position); + Optional changeAtPosition = findChange(position, changeList); + if (changeAtPosition.isEmpty()) { + appendToTextArea(targetTextview, getSeparator() + word, "unchanged"); + } else { + Change change = changeAtPosition.get(); + List changeWords = unifiedWords.subList(change.position(), change.position() + change.spanSize()); + + if (change.type() == ChangeType.DELETION) { + appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion"); + } else if (change.type() == ChangeType.ADDITION) { + if (changeInProgress) { + appendToTextArea(targetTextview, join(changeWords), "addition"); + changeInProgress = false; + } else { + appendToTextArea(targetTextview, getSeparator() + join(changeWords), "addition"); + } + } else if (change.type() == ChangeType.CHANGE_DELETION) { + appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion"); + changeInProgress = true; + } + position = position + changeWords.size() - 1; + } + } + if (targetTextview.getLength() >= getSeparator().length()) { + // There always going to be an extra separator at the start + targetTextview.deleteText(0, getSeparator().length()); + } + } + + private void appendToTextArea(StyleClassedTextArea textArea, String text, String styleClass) { + if (text.isEmpty()) { + return; + } + // Append separator without styling it + if (text.startsWith(getSeparator())) { + textArea.append(getSeparator(), "unchanged"); + textArea.append(text.substring(getSeparator().length()), styleClass); + } else { + textArea.append(text, styleClass); + } + } + + private Optional findChange(int position, List changeList) { + return changeList.stream().filter(change -> change.position() == position).findAny(); } } From d071fe41dbcc8276417b7fdf2585f5203dda6abf Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:44:12 +0100 Subject: [PATCH 058/122] Show diffs when user selects "Show Diff" in the toolbar UI - Only unified diff view will be shown for now --- .../newmergedialog/FieldRowController.java | 42 ++++++++++++++++--- .../newmergedialog/ShowDiffConfig.java | 6 +++ .../newmergedialog/ThreeWayMergeView.java | 14 +++++++ 3 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index f145ec1132d..64db8cf9fd0 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -6,7 +6,9 @@ import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.UnifiedDiffHighlighter; +import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.model.strings.StringUtil; public class FieldRowController { @@ -15,6 +17,10 @@ public class FieldRowController { private final FieldValueCell rightValueCell; private final MergedFieldCell mergedValueCell; + private final String leftValue; + + private final String rightValue; + private final ToggleGroup toggleGroup = new ToggleGroup(); public FieldRowController(String fieldName, String leftValue, String rightValue, int rowIndex) { @@ -23,6 +29,9 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, rightValueCell = new FieldValueCell(rightValue, rowIndex); mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, rowIndex); + this.leftValue = leftValue; + this.rightValue = rightValue; + toggleGroup.getToggles().addAll(leftValueCell, rightValueCell); toggleGroup.selectToggle(StringUtil.isNullOrEmpty(leftValue) ? rightValueCell : leftValueCell); toggleGroup.selectedToggleProperty().addListener(invalidated -> { @@ -53,11 +62,6 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, } else if (StringUtil.isNullOrEmpty(rightValue)) { rightValueCell.setDisable(true); } - - // Debugging - if (!leftValueCell.isDisabled() && !rightValueCell.isDisabled()) { - new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), rightValueCell.getStyleClassedLabel()).highlight(); - } } /** @@ -111,4 +115,32 @@ public boolean hasEqualLeftAndRightValues() { !StringUtil.isNullOrEmpty(rightValueCell.getText()) && leftValueCell.getText().equals(rightValueCell.getText()); } + + public void showDiffs(ShowDiffConfig diffConfig) { + // TODO: read this from diffConfig + if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) { + if (!leftValueCell.isDisabled() && !rightValueCell.isDisabled()) { + hideDiffs(); + if (diffConfig.diffMode() == ThreeWayMergeToolbar.DiffHighlightMode.WORDS) { + new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), rightValueCell.getStyleClassedLabel(), DiffHighlighter.DiffMethod.WORDS).highlight(); + } else { + new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), rightValueCell.getStyleClassedLabel(), DiffHighlighter.DiffMethod.CHARS).highlight(); + } + } + } + } + + public void hideDiffs() { + if (!StringUtil.isNullOrEmpty(leftValue)) { + int leftValueLength = getLeftValueCell().getStyleClassedLabel().getLength(); + getLeftValueCell().getStyleClassedLabel().clearStyle(0, leftValueLength); + getLeftValueCell().getStyleClassedLabel().replaceText(leftValue); + } + + if (!StringUtil.isNullOrEmpty(rightValue)) { + int rightValueLength = getRightValueCell().getStyleClassedLabel().getLength(); + getRightValueCell().getStyleClassedLabel().clearStyle(0, rightValueLength); + getRightValueCell().getStyleClassedLabel().replaceText(rightValue); + } + } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java new file mode 100644 index 00000000000..e1de4e3fc9a --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java @@ -0,0 +1,6 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; + +public record ShowDiffConfig(ThreeWayMergeToolbar.DiffView diffView, ThreeWayMergeToolbar.DiffHighlightMode diffMode) { +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 4ec81f6d6f2..f741c1515ef 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -62,6 +62,20 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry) { private void initializeToolbar() { toolbar.setOnSelectLeftEntryValuesButtonClicked(e -> fieldRowControllerList.forEach(FieldRowController::selectLeftValue)); toolbar.setOnSelectRightEntryValuesButtonClicked(e -> fieldRowControllerList.forEach(FieldRowController::selectRightValue)); + + toolbar.showDiffProperty().addListener(e -> updateDiff()); + toolbar.diffViewProperty().addListener(e -> updateDiff()); + toolbar.diffHighlightModeProperty().addListener(e -> updateDiff()); + } + + private void updateDiff() { + if (toolbar.isShowDiffEnabled()) { + fieldRowControllerList.forEach(fieldRow -> fieldRow.showDiffs( + new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightMode()) + )); + } else { + fieldRowControllerList.forEach(FieldRowController::hideDiffs); + } } private void initializeHeaderView() { From 735dc5a1b708716c197d132d2ad938131371eb1c Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:44:37 +0100 Subject: [PATCH 059/122] Fix bugs in UnifiedDiffHighlighter --- .../diffhighlighter/UnifiedDiffHighlighter.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java index 216ba52369f..38d6a92c389 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java @@ -20,10 +20,9 @@ public UnifiedDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedT } public UnifiedDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview) { - this(sourceTextview, targetTextview, DiffMethod.WORDS); + this(sourceTextview, targetTextview, DiffMethod.CHARS); } - @SuppressWarnings({"checkstyle:RegexpMultiline", "checkstyle:EmptyBlock"}) @Override public void highlight() { String sourceContent = sourceTextview.getText(); @@ -97,6 +96,10 @@ public void highlight() { position = position + changeWords.size() - 1; } } + if (targetTextview.getLength() >= getSeparator().length()) { + // There always going to be an extra separator at the start + targetTextview.deleteText(0, getSeparator().length()); + } } private void appendToTextArea(StyleClassedTextArea textArea, String text, String styleClass) { @@ -105,10 +108,11 @@ private void appendToTextArea(StyleClassedTextArea textArea, String text, String } // Append separator without styling it if (text.startsWith(getSeparator())) { - textArea.appendText(getSeparator()); + textArea.append(getSeparator(), "unchanged"); + textArea.append(text.substring(getSeparator().length()), styleClass); + } else { + textArea.append(text, styleClass); } - int separatorIndex = text.indexOf(getSeparator()); - textArea.append(text.substring(separatorIndex != -1 ? separatorIndex + 1 : 0), styleClass); } private Optional findChange(int position, List changeList) { From ff58de90937c0b6516123ad1ee96a6882473454a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:45:14 +0100 Subject: [PATCH 060/122] Style css stuff --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index 5f4731c9f27..27dc070635a 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -16,16 +16,18 @@ } .field-value:selected .selection-box { - -fx-background-color: -jr-accent-alt; - -fx-border-color: -jr-accent-alt; + /* -fx-background-color: -jr-accent-alt; + -fx-border-color: -jr-accent-alt;*/ + -fx-background-color: rgba(163, 183, 230, 0.7); + -fx-border-color: -jr-accent; } .field-value .selection-box { -fx-background-color: #0000; - -fx-border-width: 1; -fx-border-color: #0000; -fx-border-radius: 8; -fx-background-radius: 8; + -fx-border-width: 2.5; } .styled-text-area .text{ From 73454825e79aa1f4edec21ac4855cd72d6758cc2 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:45:25 +0100 Subject: [PATCH 061/122] Fix typos --- .../mergeentries/newmergedialog/ThreeWayMergeHeaderView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java index 9299c5f2ef9..4e9e15b3af5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java @@ -8,7 +8,7 @@ import org.jabref.gui.mergeentries.newmergedialog.cell.HeaderCell; /** - * I used a GridPane instead of a Hbox because Hbox allocates more space for cells + * GridPane was used instead of a Hbox because Hbox allocates more space for cells * with longer text, but I wanted all cells to have the same width */ public class ThreeWayMergeHeaderView extends GridPane { From d072bab3ee02a0e718c4e657f1839b8e5894e96b Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:50:09 +0100 Subject: [PATCH 062/122] Refactor SplitDiffHighlighter and fix bugs - Added "updated" style class for styling CHANGE diffs --- src/main/java/org/jabref/gui/Base.css | 4 + .../diffhighlighter/SplitDiffHighlighter.java | 118 ++++++------------ 2 files changed, 40 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index bc2c914258e..ccd0682d6c9 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -257,6 +257,10 @@ -rtfx-background-color:#0000; } +.updated { + -rtfx-background-color: rgba(41, 166, 236, 0.66); +} + .addition { -rtfx-background-color: rgba(29, 209, 161, 0.5); } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java index 5e3ba6274c5..5b7275220eb 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java @@ -10,9 +10,8 @@ import org.fxmisc.richtext.StyleClassedTextArea; /** - * A diff highlighter in which changes of type {@link DeltaType#CHANGE} are split between source and target - * text view. They are represented by an addition in the target text view and deletion in the source text view. - * Normal addition and deletion are kept as they are. + * A diff highlighter in which changes are split between source and target text view. + * They are represented by an addition in the target text view and deletion in the source text view. */ public final class SplitDiffHighlighter extends DiffHighlighter { @@ -20,10 +19,6 @@ public SplitDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTex super(sourceTextview, targetTextview, diffMethod); } - public SplitDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview) { - this(sourceTextview, targetTextview, DiffMethod.WORDS); - } - @Override public void highlight() { String sourceContent = sourceTextview.getText(); @@ -32,92 +27,51 @@ public void highlight() { return; } - List sourceWords = splitString(sourceContent); - List targetWords = splitString(targetContent); - List unifiedWords = new ArrayList<>(targetWords); - - List> deltaList = DiffUtils.diff(sourceWords, targetWords).getDeltas(); + List sourceTokens = splitString(sourceContent); + List targetTokens = splitString(targetContent); - List changeList = new ArrayList<>(); + List> deltaList = DiffUtils.diff(sourceTokens, targetTokens).getDeltas(); - int deletionCount = 0; for (AbstractDelta delta : deltaList) { + int affectedSourceTokensPosition = delta.getSource().getPosition(); + int affectedTargetTokensPosition = delta.getTarget().getPosition(); + + List affectedTokensInSource = delta.getSource().getLines(); + List affectedTokensInTarget = delta.getTarget().getLines(); + int joinedSourceTokensLength = affectedTokensInSource.stream() + .map(String::length) + .reduce(Integer::sum) + .map(value -> value + (getSeparator().length() * (affectedTokensInSource.size() - 1))) + .orElse(0); + + int joinedTargetTokensLength = affectedTokensInTarget.stream() + .map(String::length) + .reduce(Integer::sum) + .map(value -> value + (getSeparator().length() * (affectedTokensInTarget.size() - 1))) + .orElse(0); + int affectedSourceTokensPositionInText = getPositionInText(affectedSourceTokensPosition, sourceTokens); + int affectedTargetTokensPositionInText = getPositionInText(affectedTargetTokensPosition, targetTokens); switch (delta.getType()) { case CHANGE -> { - int changePosition = delta.getTarget().getPosition(); - int deletionPoint = changePosition + deletionCount; - int insertionPoint = deletionPoint + 1; - List deltaSourceWords = delta.getSource().getLines(); - List deltaTargetWords = delta.getTarget().getLines(); - - unifiedWords.add(deletionPoint, join(deltaSourceWords)); - - changeList.add(new Change(deletionPoint, 1, ChangeType.CHANGE_DELETION)); - changeList.add(new Change(insertionPoint, deltaTargetWords.size(), ChangeType.ADDITION)); - deletionCount++; - } - case DELETE -> { - int deletionPoint = delta.getTarget().getPosition() + deletionCount; - unifiedWords.add(deletionPoint, join(delta.getSource().getLines())); - - changeList.add(new Change(deletionPoint, 1, ChangeType.DELETION)); - deletionCount++; - } - case INSERT -> { - int insertionPoint = delta.getTarget().getPosition() + deletionCount; - changeList.add(new Change(insertionPoint, delta.getTarget().getLines().size(), ChangeType.ADDITION)); - } - } - } - sourceTextview.clear(); - targetTextview.clear(); - - boolean changeInProgress = false; - for (int position = 0; position < unifiedWords.size(); position++) { - String word = unifiedWords.get(position); - Optional changeAtPosition = findChange(position, changeList); - if (changeAtPosition.isEmpty()) { - appendToTextArea(targetTextview, getSeparator() + word, "unchanged"); - } else { - Change change = changeAtPosition.get(); - List changeWords = unifiedWords.subList(change.position(), change.position() + change.spanSize()); - - if (change.type() == ChangeType.DELETION) { - appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion"); - } else if (change.type() == ChangeType.ADDITION) { - if (changeInProgress) { - appendToTextArea(targetTextview, join(changeWords), "addition"); - changeInProgress = false; - } else { - appendToTextArea(targetTextview, getSeparator() + join(changeWords), "addition"); - } - } else if (change.type() == ChangeType.CHANGE_DELETION) { - appendToTextArea(targetTextview, getSeparator() + join(changeWords), "deletion"); - changeInProgress = true; + sourceTextview.setStyleClass(affectedSourceTokensPositionInText, affectedSourceTokensPositionInText + joinedSourceTokensLength, "deletion"); + targetTextview.setStyleClass(affectedTargetTokensPositionInText, affectedTargetTokensPositionInText + joinedTargetTokensLength, "updated"); } - position = position + changeWords.size() - 1; + case DELETE -> + sourceTextview.setStyleClass(affectedSourceTokensPositionInText, affectedSourceTokensPositionInText + joinedSourceTokensLength, "deletion"); + case INSERT -> + targetTextview.setStyleClass(affectedTargetTokensPositionInText, affectedTargetTokensPositionInText + joinedTargetTokensLength, "addition"); } } - if (targetTextview.getLength() >= getSeparator().length()) { - // There always going to be an extra separator at the start - targetTextview.deleteText(0, getSeparator().length()); - } } - private void appendToTextArea(StyleClassedTextArea textArea, String text, String styleClass) { - if (text.isEmpty()) { - return; - } - // Append separator without styling it - if (text.startsWith(getSeparator())) { - textArea.append(getSeparator(), "unchanged"); - textArea.append(text.substring(getSeparator().length()), styleClass); + public int getPositionInText(int positionInTokenList, List tokenList) { + if (positionInTokenList == 0) { + return 0; } else { - textArea.append(text, styleClass); + return tokenList.stream().limit(positionInTokenList).map(String::length) + .reduce(Integer::sum) + .map(value -> value + (getSeparator().length() * positionInTokenList)) + .orElse(0); } } - - private Optional findChange(int position, List changeList) { - return changeList.stream().filter(change -> change.position() == position).findAny(); - } } From 51879346765971c09ee79cf5f3dfca491c680767 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:51:21 +0100 Subject: [PATCH 063/122] Remove redundant constructor from UnifiedDiffHighlighter --- .../diffhighlighter/UnifiedDiffHighlighter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java index 38d6a92c389..54824f06d61 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/UnifiedDiffHighlighter.java @@ -19,10 +19,6 @@ public UnifiedDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedT super(sourceTextview, targetTextview, diffMethod); } - public UnifiedDiffHighlighter(StyleClassedTextArea sourceTextview, StyleClassedTextArea targetTextview) { - this(sourceTextview, targetTextview, DiffMethod.CHARS); - } - @Override public void highlight() { String sourceContent = sourceTextview.getText(); From cc3560fd1a5bafd5276c7a6fde9b3515686a9b55 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:53:14 +0100 Subject: [PATCH 064/122] Allow users to switch between split and unified diff view - Fixed some bugs --- .../newmergedialog/FieldRowController.java | 47 ++++++++++++------- .../newmergedialog/ShowDiffConfig.java | 3 +- .../newmergedialog/ThreeWayMergeView.java | 8 ++-- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 64db8cf9fd0..c599aebd81d 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -6,7 +6,7 @@ import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; -import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.SplitDiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.UnifiedDiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.model.strings.StringUtil; @@ -68,22 +68,32 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, * @return True if left value was selected, False otherwise */ public boolean selectLeftValue() { - if (!leftValueCell.isDisabled()) { + if (StringUtil.isNullOrEmpty(leftValue)) { + toggleGroup.selectToggle(null); + mergedValueCell.setText(""); + return false; + } else { toggleGroup.selectToggle(leftValueCell); return true; } - return false; } /** * @return True if left value was selected, False otherwise */ public boolean selectRightValue() { - if (!rightValueCell.isDisabled()) { - toggleGroup.selectToggle(rightValueCell); + if (StringUtil.isNullOrEmpty(rightValue)) { + toggleGroup.selectToggle(null); + mergedValueCell.setText(""); + return false; + } else { + if (hasEqualLeftAndRightValues()) { + toggleGroup.selectToggle(leftValueCell); + } else { + toggleGroup.selectToggle(rightValueCell); + } return true; } - return false; } public String getMergedValue() { @@ -116,21 +126,24 @@ public boolean hasEqualLeftAndRightValues() { leftValueCell.getText().equals(rightValueCell.getText()); } - public void showDiffs(ShowDiffConfig diffConfig) { - // TODO: read this from diffConfig + public void showDiff(ShowDiffConfig diffConfig) { + if (leftValueCell.isDisabled() || rightValueCell.isDisabled()) { + return; + } + // Clearing old diff styles + hideDiff(); if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) { - if (!leftValueCell.isDisabled() && !rightValueCell.isDisabled()) { - hideDiffs(); - if (diffConfig.diffMode() == ThreeWayMergeToolbar.DiffHighlightMode.WORDS) { - new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), rightValueCell.getStyleClassedLabel(), DiffHighlighter.DiffMethod.WORDS).highlight(); - } else { - new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), rightValueCell.getStyleClassedLabel(), DiffHighlighter.DiffMethod.CHARS).highlight(); - } - } + new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), + rightValueCell.getStyleClassedLabel(), + diffConfig.diffMethod()).highlight(); + } else { + new SplitDiffHighlighter(leftValueCell.getStyleClassedLabel(), + rightValueCell.getStyleClassedLabel(), + diffConfig.diffMethod()).highlight(); } } - public void hideDiffs() { + public void hideDiff() { if (!StringUtil.isNullOrEmpty(leftValue)) { int leftValueLength = getLeftValueCell().getStyleClassedLabel().getLength(); getLeftValueCell().getStyleClassedLabel().clearStyle(0, leftValueLength); diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java index e1de4e3fc9a..05a4d0121a9 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java @@ -1,6 +1,7 @@ package org.jabref.gui.mergeentries.newmergedialog; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; -public record ShowDiffConfig(ThreeWayMergeToolbar.DiffView diffView, ThreeWayMergeToolbar.DiffHighlightMode diffMode) { +public record ShowDiffConfig(ThreeWayMergeToolbar.DiffView diffView, DiffHighlighter.DiffMethod diffMethod) { } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index f741c1515ef..efd7ea93316 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -10,6 +10,7 @@ import javafx.scene.layout.VBox; import javafx.stage.Screen; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; @@ -70,11 +71,10 @@ private void initializeToolbar() { private void updateDiff() { if (toolbar.isShowDiffEnabled()) { - fieldRowControllerList.forEach(fieldRow -> fieldRow.showDiffs( - new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightMode()) - )); + DiffHighlighter.DiffMethod diffMethod = toolbar.getDiffHighlightMode() == ThreeWayMergeToolbar.DiffHighlightMode.WORDS ? DiffHighlighter.DiffMethod.WORDS : DiffHighlighter.DiffMethod.CHARS; + fieldRowControllerList.forEach(fieldRow -> fieldRow.showDiff(new ShowDiffConfig(toolbar.getDiffView(), diffMethod))); } else { - fieldRowControllerList.forEach(FieldRowController::hideDiffs); + fieldRowControllerList.forEach(FieldRowController::hideDiff); } } From b56e5d045b9c17df91f876f10e859024ba4361c1 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:53:58 +0100 Subject: [PATCH 065/122] Set left and right headers in MergeEntriesDialog --- .../org/jabref/gui/mergeentries/MergeEntriesDialog.java | 7 ++++--- .../mergeentries/newmergedialog/ThreeWayMergeView.java | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index b70a2dc915e..34dcba8b6db 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -1,5 +1,6 @@ package org.jabref.gui.mergeentries; +import javafx.geometry.Insets; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.stage.StageStyle; @@ -23,7 +24,7 @@ public MergeEntriesDialog(BibEntry one, BibEntry two) { */ private void init() { this.getDialogPane().setContent(threeWayMergeView); - this.initStyle(StageStyle.UTILITY); + this.getDialogPane().setPadding(new Insets(0)); // Create buttons ButtonType replaceEntries = new ButtonType(Localization.lang("Merge entries"), ButtonBar.ButtonData.OK_DONE); @@ -38,10 +39,10 @@ private void init() { } public void setLeftHeaderText(String leftHeaderText) { - // mergeEntries.setLeftHeaderText(leftHeaderText); + threeWayMergeView.setLeftHeader(leftHeaderText); } public void setRightHeaderText(String rightHeaderText) { - // mergeEntries.setRightHeaderText(rightHeaderText); + threeWayMergeView.setRightHeader(rightHeaderText); } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index efd7ea93316..506bdffcb3f 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -148,4 +148,12 @@ private void addFieldRow(int index) { public BibEntry getMergedEntry() { return viewModel.getMergedEntry(); } + + public void setLeftHeader(String leftHeader) { + headerView.setLeftHeader(leftHeader); + } + + public void setRightHeader(String rightHeader) { + headerView.setRightHeader(rightHeader); + } } From d09808b43e6c19c65980a6e7568d8176c8881251 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:54:14 +0100 Subject: [PATCH 066/122] Refactor ThreeWayMergeHeaderView --- .../newmergedialog/ThreeWayMergeHeaderView.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java index 4e9e15b3af5..05acddd5017 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java @@ -36,7 +36,10 @@ public ThreeWayMergeHeaderView(String leftHeader, String rightHeader) { setMaxHeight(Control.USE_PREF_SIZE); setMinHeight(Control.USE_PREF_SIZE); - bindHeaderWidthToFieldGridWidth(); + // The fields grid pane is contained within a scroll pane, thus it doesn't allocate the full available width. In + // fact, it uses the available width minus the size of the scrollbar which is 8. This leads to header columns being + // always wider than fields columns. This hack should fix it. + setPadding(new Insets(0, 8, 0, 0)); } public void setLeftHeader(String leftHeader) { @@ -46,13 +49,4 @@ public void setLeftHeader(String leftHeader) { public void setRightHeader(String rightHeader) { rightHeaderCell.setText(rightHeader); } - - /** - * The fields grid pane is contained within a scroll pane, thus it doesn't allocate the full available width. In - * fact, it uses the available width minus the size of the scrollbar which is 8. This leads to header columns being - * always larger than fields columns. This hack should fix it. - */ - private void bindHeaderWidthToFieldGridWidth() { - setPadding(new Insets(0, 8, 0, 0)); - } } From 5c0b51fda256ffd0b9b009adb65625448dfd2f0f Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 28 Jun 2022 22:02:34 +0100 Subject: [PATCH 067/122] Remove wellbehavedfx module dependency --- src/main/java/module-info.java | 1 - .../newmergedialog/cell/FieldValueCell.java | 26 ++++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 68a5a571fbd..751863df3e1 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -117,7 +117,6 @@ requires net.harawata.appdirs; requires org.eclipse.jgit; - requires wellbehavedfx; uses org.eclipse.jgit.transport.SshSessionFactory; uses org.eclipse.jgit.lib.GpgSigner; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 649d95e20d9..e109c88caf2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -5,7 +5,6 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.css.PseudoClass; -import javafx.event.Event; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Cursor; @@ -29,15 +28,9 @@ import org.fxmisc.flowless.VirtualizedScrollPane; import org.fxmisc.richtext.StyleClassedTextArea; -import org.fxmisc.wellbehaved.event.InputMap; -import org.fxmisc.wellbehaved.event.Nodes; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.materialdesign2.MaterialDesignC; -import static org.fxmisc.wellbehaved.event.EventPattern.anyOf; -import static org.fxmisc.wellbehaved.event.EventPattern.eventType; -import static org.fxmisc.wellbehaved.event.EventPattern.mousePressed; - /** * A readonly, selectable field cell that contains the value of some field */ @@ -143,14 +136,17 @@ private void initializeScrollPane() { } private void preventTextSelectionViaMouseEvents() { - InputMap preventSelection = InputMap.consume( - anyOf(eventType(MouseEvent.MOUSE_DRAGGED), - eventType(MouseEvent.DRAG_DETECTED), - eventType(MouseEvent.MOUSE_ENTERED), - mousePressed().unless(e -> e.getClickCount() == 1) - ) - ); - Nodes.addInputMap(label, preventSelection); + label.addEventFilter(MouseEvent.ANY, e -> { + if (e.getEventType() == MouseEvent.MOUSE_DRAGGED || + e.getEventType() == MouseEvent.DRAG_DETECTED || + e.getEventType() == MouseEvent.MOUSE_ENTERED) { + e.consume(); + } else if (e.getEventType() == MouseEvent.MOUSE_PRESSED) { + if (e.getClickCount() > 1) { + e.consume(); + } + } + }); } @Override From 0c0db6a5ed9673500be7fd8b402cbf4ca78adcef Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:58:48 +0100 Subject: [PATCH 068/122] Delete EmptyCell.java - I might recreate it later, but for now, I'm choosing simplicity over performance --- .../newmergedialog/ThreeWayMergeHeaderView.java | 3 +-- .../mergeentries/newmergedialog/cell/EmptyCell.java | 13 ------------- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java index 05acddd5017..a3b33e99461 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java @@ -4,7 +4,6 @@ import javafx.scene.control.Control; import javafx.scene.layout.GridPane; -import org.jabref.gui.mergeentries.newmergedialog.cell.EmptyCell; import org.jabref.gui.mergeentries.newmergedialog.cell.HeaderCell; /** @@ -26,7 +25,7 @@ public ThreeWayMergeHeaderView(String leftHeader, String rightHeader) { this.mergedHeaderCell = new HeaderCell("Merged Entry"); addRow(0, - new EmptyCell(HeaderCell.DEFAULT_STYLE_CLASS), + new HeaderCell(""), leftHeaderCell, rightHeaderCell, mergedHeaderCell diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java deleted file mode 100644 index e52ada18b13..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/EmptyCell.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.jabref.gui.mergeentries.newmergedialog.cell; - -public class EmptyCell extends AbstractCell { - public EmptyCell(String styleClass, int rowIndex) { - super("", rowIndex); - getStyleClass().add(styleClass); - setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); - } - - public EmptyCell(String styleClass) { - this(styleClass, AbstractCell.NO_ROW_NUMBER); - } -} From d854958067a59fe2697378d0eb93dccd841a070e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 04:59:46 +0100 Subject: [PATCH 069/122] Refactor FieldValueCell --- .../newmergedialog/cell/FieldValueCell.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index e109c88caf2..cde5ae3ec9a 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -116,18 +116,23 @@ private void initializeSelectionBox() { labelBox.setPadding(new Insets(8)); labelBox.setCursor(Cursor.HAND); + checkmarkLayout.getChildren().setAll(createCopyButton()); + checkmarkLayout.setAlignment(Pos.TOP_CENTER); + checkmarkLayout.setPrefWidth(28); + + } + + private Button createCopyButton() { FontIcon copyIcon = FontIcon.of(MaterialDesignC.CONTENT_COPY); + copyIcon.getStyleClass().add("copy-icon"); + Button copyButton = factory.createIconButton(() -> Localization.lang("Copy"), new CopyFieldValueCommand(Globals.prefs, getText())); copyButton.setGraphic(copyIcon); copyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); - - copyIcon.getStyleClass().add("copy-icon"); - checkmarkLayout.getChildren().setAll(copyButton); - checkmarkLayout.setAlignment(Pos.TOP_CENTER); - checkmarkLayout.setPrefWidth(28); - copyButton.setMaxHeight(Double.MAX_VALUE); VBox.setVgrow(copyButton, Priority.ALWAYS); + + return copyButton; } private void initializeScrollPane() { From 1fe169738b4f87bd9650df46d303f5d921e6f873 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:00:34 +0100 Subject: [PATCH 070/122] Use the new merge UI in DuplicateResolverDialog --- .../DuplicateResolverDialog.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java index 1dad9cae973..822c63bf77f 100644 --- a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java +++ b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java @@ -11,6 +11,7 @@ import org.jabref.gui.duplicationFinder.DuplicateResolverDialog.DuplicateResolverResult; import org.jabref.gui.help.HelpAction; import org.jabref.gui.mergeentries.MergeEntries; +import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView; import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.DialogWindowState; import org.jabref.logic.help.HelpFile; @@ -39,7 +40,7 @@ public enum DuplicateResolverResult { BREAK } - private MergeEntries mergeEntries; + private ThreeWayMergeView threeWayMerge; private final DialogService dialogService; public DuplicateResolverDialog(BibEntry one, BibEntry two, DuplicateResolverType type, BibDatabaseContext database, StateManager stateManager, DialogService dialogService) { @@ -69,14 +70,14 @@ private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { first = new ButtonType(Localization.lang("Keep left"), ButtonData.APPLY); second = new ButtonType(Localization.lang("Keep right"), ButtonData.APPLY); both = new ButtonType(Localization.lang("Keep both"), ButtonData.APPLY); - mergeEntries = new MergeEntries(one, two); + threeWayMerge = new ThreeWayMergeView(one, two); break; case INSPECTION: first = new ButtonType(Localization.lang("Remove old entry"), ButtonData.APPLY); second = new ButtonType(Localization.lang("Remove entry from import"), ButtonData.APPLY); both = new ButtonType(Localization.lang("Keep both"), ButtonData.APPLY); - mergeEntries = new MergeEntries(one, two, Localization.lang("Old entry"), - Localization.lang("From import")); + threeWayMerge = new ThreeWayMergeView(one, two, Localization.lang("Old entry"), + Localization.lang("From import")); break; case DUPLICATE_SEARCH_WITH_EXACT: first = new ButtonType(Localization.lang("Keep left"), ButtonData.APPLY); @@ -85,14 +86,14 @@ private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { removeExactVisible = true; - mergeEntries = new MergeEntries(one, two); + threeWayMerge = new ThreeWayMergeView(one, two); break; default: first = new ButtonType(Localization.lang("Import and remove old entry"), ButtonData.APPLY); second = new ButtonType(Localization.lang("Do not import entry"), ButtonData.APPLY); both = new ButtonType(Localization.lang("Import and keep old entry"), ButtonData.APPLY); - mergeEntries = new MergeEntries(one, two, Localization.lang("Old entry"), - Localization.lang("From import")); + threeWayMerge = new ThreeWayMergeView(one, two, Localization.lang("Old entry"), + Localization.lang("From import")); break; } if (removeExactVisible) { @@ -109,7 +110,7 @@ private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { this.setY(state.getY()); } - BorderPane borderPane = new BorderPane(mergeEntries); + BorderPane borderPane = new BorderPane(threeWayMerge); borderPane.setBottom(options); this.setResultConverter(button -> { @@ -136,6 +137,6 @@ private void init(BibEntry one, BibEntry two, DuplicateResolverType type) { } public BibEntry getMergedEntry() { - return mergeEntries.getMergedEntry(); + return threeWayMerge.getMergedEntry(); } } From b09933a0c4d41618ec1a97eaa19bca08db0f22f4 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:04:43 +0100 Subject: [PATCH 071/122] Expose an API to change diff view and highlight method from outside the merge toolbar --- .../newmergedialog/cell/FieldValueCell.java | 1 - .../toolbar/ThreeWayMergeToolbar.java | 42 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index cde5ae3ec9a..d511d57d427 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -119,7 +119,6 @@ private void initializeSelectionBox() { checkmarkLayout.getChildren().setAll(createCopyButton()); checkmarkLayout.setAlignment(Pos.TOP_CENTER); checkmarkLayout.setPrefWidth(28); - } private Button createCopyButton() { diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java index 0703f49f7c2..d0f13f1d95f 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java @@ -1,7 +1,7 @@ package org.jabref.gui.mergeentries.newmergedialog.toolbar; import java.util.Arrays; -import java.util.function.Consumer; +import java.util.Objects; import javafx.beans.binding.BooleanExpression; import javafx.beans.property.ObjectProperty; @@ -12,13 +12,17 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.RadioButton; import javafx.scene.control.ToggleGroup; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.util.StringConverter; +import org.jabref.gui.mergeentries.DiffHighlighting; +import org.jabref.gui.mergeentries.DiffMode; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; + import com.airhacks.afterburner.views.ViewLoader; import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBinding; +import org.apache.commons.lang3.builder.Diff; public class ThreeWayMergeToolbar extends AnchorPane { @FXML @@ -98,7 +102,7 @@ public DiffView fromString(String string) { diffHighlightModeToggleGroup.selectToggle(highlightWordsRadioButton); } - public ReadOnlyObjectProperty diffViewProperty() { + public ObjectProperty diffViewProperty() { return diffViewComboBox.valueProperty(); } @@ -106,18 +110,26 @@ public DiffView getDiffView() { return diffViewProperty().get(); } + public void setDiffView(DiffView diffView) { + diffViewProperty().set(diffView); + } + public EasyBinding showDiffProperty() { return showDiff; } + public void setShowDiff(boolean showDiff) { + plainTextOrDiffComboBox.valueProperty().set(showDiff ? PlainTextOrDiff.Diff : PlainTextOrDiff.PLAIN_TEXT); + } + /** - * Convince method used to disable diff related views when diff is not selected. + * Convenience method used to disable diff related views when diff is not selected. * *

* This method is required because {@link EasyBinding} class doesn't have a method to invert a boolean property, * like {@link BooleanExpression#not()} *

- * */ + */ public EasyBinding notShowDiffProperty() { return showDiffProperty().map(showDiff -> !showDiff); } @@ -134,12 +146,16 @@ public DiffHighlightMode getDiffHighlightMode() { return diffHighlightModeProperty().get(); } - public void setOnSelectLeftEntryValuesButtonClicked(Consumer onClick) { - selectLeftEntryValuesButton.setOnMouseClicked(onClick::accept); + public void setDiffHighlightMode(DiffHighlightMode diffHighlightMode) { + diffHighlightModeProperty().set(diffHighlightMode); } - public void setOnSelectRightEntryValuesButtonClicked(Consumer onClick) { - selectRightEntryValuesButton.setOnMouseClicked(onClick::accept); + public void setOnSelectLeftEntryValuesButtonClicked(Runnable onClick) { + selectLeftEntryValuesButton.setOnMouseClicked(e -> onClick.run()); + } + + public void setOnSelectRightEntryValuesButtonClicked(Runnable onClick) { + selectRightEntryValuesButton.setOnMouseClicked(e -> onClick.run()); } public enum PlainTextOrDiff { @@ -184,7 +200,13 @@ public static DiffView fromString(String str) { } } + // TODO: remove this and use DiffMethod public enum DiffHighlightMode { - WORDS, CHARS + WORDS, CHARS; + + public static DiffHighlightMode from(DiffHighlighter.DiffMethod diffMethod) { + Objects.requireNonNull(diffMethod, "Diff method is required"); + return diffMethod == DiffHighlighter.DiffMethod.WORDS ? WORDS : CHARS; + } } } From 2f39c4eb7ca14f75b27c26769030095204dfe21d Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:05:27 +0100 Subject: [PATCH 072/122] Use the new merge UI when an entry is changed from external --- .../gui/collab/EntryChangeViewModel.java | 22 +++++++++++-------- .../newmergedialog/ThreeWayMergeView.java | 20 ++++++++++++++--- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java b/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java index 3ebed73f187..421e4c1fcd9 100644 --- a/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java +++ b/src/main/java/org/jabref/gui/collab/EntryChangeViewModel.java @@ -5,8 +5,10 @@ import javafx.scene.control.Label; import javafx.scene.layout.VBox; -import org.jabref.gui.mergeentries.MergeEntries; -import org.jabref.gui.mergeentries.MergeEntries.DefaultRadioButtonSelectionMode; +import org.jabref.gui.mergeentries.newmergedialog.ShowDiffConfig; +import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView; +import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; +import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableInsertEntries; import org.jabref.logic.l10n.Localization; @@ -17,7 +19,7 @@ class EntryChangeViewModel extends DatabaseChangeViewModel { private final BibEntry oldEntry; private final BibEntry newEntry; - private MergeEntries mergePanel; + private ThreeWayMergeView threeWayMergeView; public EntryChangeViewModel(BibEntry entry, BibEntry newEntry) { super(); @@ -37,9 +39,9 @@ public EntryChangeViewModel(BibEntry entry, BibEntry newEntry) { public void setAccepted(boolean accepted) { super.setAccepted(accepted); if (accepted) { - mergePanel.selectAllRightRadioButtons(); + threeWayMergeView.selectRightEntryValues(); } else { - mergePanel.selectAllLeftRadioButtons(); + threeWayMergeView.selectLeftEntryValues(); } } @@ -47,7 +49,7 @@ public void setAccepted(boolean accepted) { public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) { this.description(); // Init dialog to prevent NPE database.getDatabase().removeEntry(oldEntry); - BibEntry mergedEntry = mergePanel.getMergedEntry(); + BibEntry mergedEntry = threeWayMergeView.getMergedEntry(); mergedEntry.setId(oldEntry.getId()); // Keep ID database.getDatabase().insertEntry(mergedEntry); undoEdit.addEdit(new UndoableInsertEntries(database.getDatabase(), oldEntry)); @@ -56,13 +58,15 @@ public void makeChange(BibDatabaseContext database, NamedCompound undoEdit) { @Override public Node description() { - mergePanel = new MergeEntries(oldEntry, newEntry, Localization.lang("In JabRef"), Localization.lang("On disk"), DefaultRadioButtonSelectionMode.LEFT); + threeWayMergeView = new ThreeWayMergeView(oldEntry, newEntry, Localization.lang("In JabRef"), Localization.lang("On disk")); + threeWayMergeView.selectLeftEntryValues(); + threeWayMergeView.showDiff(new ShowDiffConfig(ThreeWayMergeToolbar.DiffView.SPLIT, DiffHighlighter.DiffMethod.WORDS)); VBox container = new VBox(10); Label header = new Label(name); header.getStyleClass().add("sectionHeader"); container.getChildren().add(header); - container.getChildren().add(mergePanel); - VBox.setMargin(mergePanel, new Insets(5, 5, 5, 5)); + container.getChildren().add(threeWayMergeView); + VBox.setMargin(threeWayMergeView, new Insets(5, 5, 5, 5)); return container; } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 506bdffcb3f..ae7aa604904 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -50,7 +50,7 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHea initializeHeaderView(); initializeToolbar(); - this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.80); + this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.75); this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.95); getChildren().addAll(toolbar, headerView, scrollPane); @@ -61,8 +61,8 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry) { } private void initializeToolbar() { - toolbar.setOnSelectLeftEntryValuesButtonClicked(e -> fieldRowControllerList.forEach(FieldRowController::selectLeftValue)); - toolbar.setOnSelectRightEntryValuesButtonClicked(e -> fieldRowControllerList.forEach(FieldRowController::selectRightValue)); + toolbar.setOnSelectLeftEntryValuesButtonClicked(this::selectLeftEntryValues); + toolbar.setOnSelectRightEntryValuesButtonClicked(this::selectRightEntryValues); toolbar.showDiffProperty().addListener(e -> updateDiff()); toolbar.diffViewProperty().addListener(e -> updateDiff()); @@ -156,4 +156,18 @@ public void setLeftHeader(String leftHeader) { public void setRightHeader(String rightHeader) { headerView.setRightHeader(rightHeader); } + + public void selectLeftEntryValues() { + fieldRowControllerList.forEach(FieldRowController::selectLeftValue); + } + + public void selectRightEntryValues() { + fieldRowControllerList.forEach(FieldRowController::selectRightValue); + } + + public void showDiff(ShowDiffConfig diffConfig) { + toolbar.setDiffView(diffConfig.diffView()); + toolbar.setDiffHighlightMode(ThreeWayMergeToolbar.DiffHighlightMode.from(diffConfig.diffMethod())); + toolbar.setShowDiff(true); + } } From a8b2aed3c1231007b203ebd313cb5a8f6fcb4545 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Wed, 29 Jun 2022 17:51:51 +0100 Subject: [PATCH 073/122] Minor styling --- .../org/jabref/gui/mergeentries/MergeEntriesDialog.java | 6 +++--- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.css | 2 +- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index 34dcba8b6db..3486a9702d2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -1,9 +1,7 @@ package org.jabref.gui.mergeentries; -import javafx.geometry.Insets; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; -import javafx.stage.StageStyle; import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView; import org.jabref.gui.util.BaseDialog; @@ -23,8 +21,10 @@ public MergeEntriesDialog(BibEntry one, BibEntry two) { * Sets up the dialog */ private void init() { + this.setX(20); + this.setY(20); + this.getDialogPane().setContent(threeWayMergeView); - this.getDialogPane().setPadding(new Insets(0)); // Create buttons ButtonType replaceEntries = new ButtonType(Localization.lang("Merge entries"), ButtonBar.ButtonData.OK_DONE); diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index 27dc070635a..b776ff345c6 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -18,7 +18,7 @@ .field-value:selected .selection-box { /* -fx-background-color: -jr-accent-alt; -fx-border-color: -jr-accent-alt;*/ - -fx-background-color: rgba(163, 183, 230, 0.7); + -fx-background-color: rgba(163, 183, 230, 0.80); -fx-border-color: -jr-accent; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index ae7aa604904..1474fc249b6 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -50,8 +50,8 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHea initializeHeaderView(); initializeToolbar(); - this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.75); - this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.95); + this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.80); + this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.97); getChildren().addAll(toolbar, headerView, scrollPane); } From aaf77addd27e5d0b654f0d62c07755cf6d13a487 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:06:43 +0100 Subject: [PATCH 074/122] Remove DiffHighlighter.css stylesheet - You can find diff styles in Base.css and Dark.css --- .../newmergedialog/diffhighlighter/DiffHighlighter.css | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.css diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/DiffHighlighter.css deleted file mode 100644 index e69de29bb2d..00000000000 From c96b122cfc26b8255107f88772f77d06b93cddf6 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:08:14 +0100 Subject: [PATCH 075/122] Remove unused dependencies --- .../newmergedialog/toolbar/ThreeWayMergeToolbar.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java index d0f13f1d95f..5c37e61a837 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java @@ -5,7 +5,6 @@ import javafx.beans.binding.BooleanExpression; import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -15,14 +14,11 @@ import javafx.scene.layout.AnchorPane; import javafx.util.StringConverter; -import org.jabref.gui.mergeentries.DiffHighlighting; -import org.jabref.gui.mergeentries.DiffMode; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; import com.airhacks.afterburner.views.ViewLoader; import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBinding; -import org.apache.commons.lang3.builder.Diff; public class ThreeWayMergeToolbar extends AnchorPane { @FXML From f8abc0fa77074697b165c5bbbe02e8176d4dbd7c Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:08:49 +0100 Subject: [PATCH 076/122] Allow selecting empty field values --- .../newmergedialog/FieldRowController.java | 84 ++++++------------- .../newmergedialog/ThreeWayMergeView.java | 3 +- 2 files changed, 28 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index c599aebd81d..24a949b230e 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -11,6 +11,8 @@ import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.model.strings.StringUtil; +import org.fxmisc.richtext.StyleClassedTextArea; + public class FieldRowController { private final FieldNameCell fieldNameCell; private final FieldValueCell leftValueCell; @@ -41,58 +43,31 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, }); mergedValueCell.textProperty().addListener((observable, old, mergedValue) -> { - if (!StringUtil.isNullOrEmpty(mergedValue)) { - if (mergedValue.equals(leftValue)) { - toggleGroup.selectToggle(leftValueCell); - } else if (mergedValue.equals(rightValue)) { - toggleGroup.selectToggle(rightValueCell); - } else { - // deselect all toggles because left and right values don't equal the merged value - toggleGroup.selectToggle(null); - } + if (mergedValue.equals(leftValue)) { + toggleGroup.selectToggle(leftValueCell); + } else if (mergedValue.equals(rightValue)) { + toggleGroup.selectToggle(rightValueCell); } else { - // deselect all toggles because empty toggles cannot be selected + // deselect all toggles because left and right values don't equal the merged value toggleGroup.selectToggle(null); } }); - // empty toggles are disabled and cannot be selected - if (StringUtil.isNullOrEmpty(leftValue)) { - leftValueCell.setDisable(true); - } else if (StringUtil.isNullOrEmpty(rightValue)) { + // Because when both left and right have the same values, then only left value will be shown + if (hasEqualLeftAndRightValues()) { rightValueCell.setDisable(true); } } - /** - * @return True if left value was selected, False otherwise - */ - public boolean selectLeftValue() { - if (StringUtil.isNullOrEmpty(leftValue)) { - toggleGroup.selectToggle(null); - mergedValueCell.setText(""); - return false; - } else { - toggleGroup.selectToggle(leftValueCell); - return true; - } + public void selectLeftValue() { + toggleGroup.selectToggle(leftValueCell); } - /** - * @return True if left value was selected, False otherwise - */ - public boolean selectRightValue() { - if (StringUtil.isNullOrEmpty(rightValue)) { - toggleGroup.selectToggle(null); - mergedValueCell.setText(""); - return false; + public void selectRightValue() { + if (rightValueCell.isDisabled()) { + selectLeftValue(); } else { - if (hasEqualLeftAndRightValues()) { - toggleGroup.selectToggle(leftValueCell); - } else { - toggleGroup.selectToggle(rightValueCell); - } - return true; + toggleGroup.selectToggle(rightValueCell); } } @@ -130,30 +105,25 @@ public void showDiff(ShowDiffConfig diffConfig) { if (leftValueCell.isDisabled() || rightValueCell.isDisabled()) { return; } - // Clearing old diff styles + + StyleClassedTextArea leftLabel = leftValueCell.getStyleClassedLabel(); + StyleClassedTextArea rightLabel = rightValueCell.getStyleClassedLabel(); + // Clearing old diff styles based on previous diffConfig hideDiff(); if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) { - new UnifiedDiffHighlighter(leftValueCell.getStyleClassedLabel(), - rightValueCell.getStyleClassedLabel(), - diffConfig.diffMethod()).highlight(); + new UnifiedDiffHighlighter(leftLabel, rightLabel, diffConfig.diffMethod()).highlight(); } else { - new SplitDiffHighlighter(leftValueCell.getStyleClassedLabel(), - rightValueCell.getStyleClassedLabel(), - diffConfig.diffMethod()).highlight(); + new SplitDiffHighlighter(leftLabel, rightLabel, diffConfig.diffMethod()).highlight(); } } public void hideDiff() { - if (!StringUtil.isNullOrEmpty(leftValue)) { - int leftValueLength = getLeftValueCell().getStyleClassedLabel().getLength(); - getLeftValueCell().getStyleClassedLabel().clearStyle(0, leftValueLength); - getLeftValueCell().getStyleClassedLabel().replaceText(leftValue); - } + int leftValueLength = getLeftValueCell().getStyleClassedLabel().getLength(); + getLeftValueCell().getStyleClassedLabel().clearStyle(0, leftValueLength); + getLeftValueCell().getStyleClassedLabel().replaceText(leftValue); - if (!StringUtil.isNullOrEmpty(rightValue)) { - int rightValueLength = getRightValueCell().getStyleClassedLabel().getLength(); - getRightValueCell().getStyleClassedLabel().clearStyle(0, rightValueLength); - getRightValueCell().getStyleClassedLabel().replaceText(rightValue); - } + int rightValueLength = getRightValueCell().getStyleClassedLabel().getLength(); + getRightValueCell().getStyleClassedLabel().clearStyle(0, rightValueLength); + getRightValueCell().getStyleClassedLabel().replaceText(rightValue); } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 1474fc249b6..fe77ea972c5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -50,7 +50,7 @@ public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHea initializeHeaderView(); initializeToolbar(); - this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.80); + this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.76); this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.97); getChildren().addAll(toolbar, headerView, scrollPane); @@ -139,7 +139,6 @@ private void addFieldRow(int index) { mergeGridPane.add(fieldRow.getFieldNameCell(), 0, index, 1, 1); mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index, 2, 1); mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index, 1, 1); - fieldRow.getRightValueCell().setDisable(true); } else { mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); } From 7af8944d14ae67f6d40f351a8c67a39de9bff463 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:15:37 +0100 Subject: [PATCH 077/122] Fix readme :( --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08b68588cfb..23cc6f637f6 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,6 @@ For IntelliJ IDEA, just import the project via a Gradle Import by pointing at th ## Sponsoring -JabRef development is by powered YourKit Java Profiler [![YourKit Java Profiler](https://www.yourkit.com/images/yk_logo.png)](https://www.yourkit.com/java/profiler/) +JabRef development is powered by YourKit Java Profiler [![YourKit Java Profiler](https://www.yourkit.com/images/yk_logo.png)](https://www.yourkit.com/java/profiler/) [JabRef]: https://www.jabref.org From 4e2e2d23bd4e82fb7044a7e18c4078b7c67991ad Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 2 Jul 2022 05:31:03 +0100 Subject: [PATCH 078/122] Use a theme color for selection box background --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index b776ff345c6..59963812688 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -18,7 +18,7 @@ .field-value:selected .selection-box { /* -fx-background-color: -jr-accent-alt; -fx-border-color: -jr-accent-alt;*/ - -fx-background-color: rgba(163, 183, 230, 0.80); + -fx-background-color: -jr-accent-alt; -fx-border-color: -jr-accent; } From 30e817b770ed097bfc14a7c286bfcb94adf89c5e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sun, 3 Jul 2022 18:33:48 +0100 Subject: [PATCH 079/122] Refactor FieldValueCell --- .../newmergedialog/cell/FieldValueCell.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index d511d57d427..08ad2b33ff4 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -65,7 +65,7 @@ protected void invalidated() { } }; private final HBox selectionBox = new HBox(); - private final VBox checkmarkLayout = new VBox(); + private final HBox actionsContainer = new HBox(); public FieldValueCell(String text, int rowIndex) { super(text, rowIndex); @@ -84,7 +84,7 @@ private void initialize() { } }); - selectionBox.getChildren().addAll(labelBox, checkmarkLayout); + selectionBox.getChildren().addAll(labelBox, actionsContainer); getChildren().setAll(selectionBox); } @@ -116,9 +116,9 @@ private void initializeSelectionBox() { labelBox.setPadding(new Insets(8)); labelBox.setCursor(Cursor.HAND); - checkmarkLayout.getChildren().setAll(createCopyButton()); - checkmarkLayout.setAlignment(Pos.TOP_CENTER); - checkmarkLayout.setPrefWidth(28); + actionsContainer.getChildren().setAll(createCopyButton()); + actionsContainer.setAlignment(Pos.TOP_CENTER); + actionsContainer.setPrefWidth(28); } private Button createCopyButton() { @@ -129,7 +129,6 @@ private Button createCopyButton() { copyButton.setGraphic(copyIcon); copyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); copyButton.setMaxHeight(Double.MAX_VALUE); - VBox.setVgrow(copyButton, Priority.ALWAYS); return copyButton; } From c8a9e31b4e76bfbfdea905379e3a09ac2d2c2795 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sun, 3 Jul 2022 19:40:16 +0100 Subject: [PATCH 080/122] Add an action to FieldValueCell for opening Urls --- src/main/java/org/jabref/gui/Base.css | 8 ++-- src/main/java/org/jabref/gui/Dark.css | 8 ++-- .../{ => cell}/CopyFieldValueCommand.java | 2 +- .../newmergedialog/cell/FieldValueCell.java | 43 ++++++++++++++++--- .../cell/OpenExternalLinkAction.java | 32 ++++++++++++++ 5 files changed, 77 insertions(+), 16 deletions(-) rename src/main/java/org/jabref/gui/mergeentries/newmergedialog/{ => cell}/CopyFieldValueCommand.java (92%) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index ccd0682d6c9..98b8fc4df8e 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -632,18 +632,18 @@ TextFlow > .tooltip-text-monospaced { -fx-background-insets: 0; } -.field-value .copy-icon { - -fx-icon-size: 15; +.field-value .action-icon { + -fx-icon-size: 14; -fx-icon-color: -fx-text-background-color; -fx-blend-mode: multiply ; -fx-opacity: 69%; } -.field-value:disabled .copy-icon { +.field-value:disabled .action-icon { -fx-opacity: 0%; } -.field-value:selected .copy-icon { +.field-value:selected .action-icon { } diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 63bd21c0a99..21ec0ac6b57 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -93,18 +93,18 @@ .tree-table-row-cell:hover { -fx-background-color: -jr-hover; } -.field-value .copy-icon { - -fx-icon-size: 15; +.field-value .action-icon { + -fx-icon-size: 14; -fx-icon-color: -fx-text-background-color; -fx-blend-mode: inherit; -fx-opacity: 100%; } -.field-value:disabled .copy-icon { +.field-value:disabled .action-icon { -fx-opacity: 0%; } -.field-value:selected .copy-icon { +.field-value:selected .action-icon { -fx-icon-color: -fx-text-background-color; -fx-opacity: 100%; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/CopyFieldValueCommand.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java similarity index 92% rename from src/main/java/org/jabref/gui/mergeentries/newmergedialog/CopyFieldValueCommand.java rename to src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java index a3ef9024215..e18a831933b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/CopyFieldValueCommand.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/CopyFieldValueCommand.java @@ -1,4 +1,4 @@ -package org.jabref.gui.mergeentries.newmergedialog; +package org.jabref.gui.mergeentries.newmergedialog.cell; import java.util.Objects; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 08ad2b33ff4..acb1cb4d600 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -3,11 +3,13 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Cursor; +import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ContentDisplay; import javafx.scene.control.ScrollPane; @@ -18,23 +20,29 @@ import javafx.scene.layout.Background; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import org.jabref.gui.Globals; import org.jabref.gui.actions.ActionFactory; -import org.jabref.gui.mergeentries.newmergedialog.CopyFieldValueCommand; +import org.jabref.gui.fieldeditors.URLUtil; +import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; +import org.jabref.model.strings.StringUtil; +import com.tobiasdiez.easybind.EasyBind; import org.fxmisc.flowless.VirtualizedScrollPane; import org.fxmisc.richtext.StyleClassedTextArea; import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.materialdesign2.MaterialDesignC; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A readonly, selectable field cell that contains the value of some field */ public class FieldValueCell extends AbstractCell implements Toggle { + public static final Logger LOGGER = LoggerFactory.getLogger(FieldValueCell.class); + public static final String DEFAULT_STYLE_CLASS = "field-value"; public static final String SELECTION_BOX_STYLE_CLASS = "selection-box"; @@ -67,6 +75,8 @@ protected void invalidated() { private final HBox selectionBox = new HBox(); private final HBox actionsContainer = new HBox(); + private final BooleanProperty isUrl = new SimpleBooleanProperty(); + public FieldValueCell(String text, int rowIndex) { super(text, rowIndex); initialize(); @@ -77,6 +87,7 @@ private void initialize() { initializeScrollPane(); initializeLabel(); initializeSelectionBox(); + initializeActions(); textProperty().addListener(invalidated -> setUserData(getText())); setOnMouseClicked(e -> { if (!isDisabled()) { @@ -108,6 +119,12 @@ private void initializeLabel() { }); } + private void initializeActions() { + actionsContainer.getChildren().setAll(createOpenLinkButton(), createCopyButton()); + actionsContainer.setAlignment(Pos.TOP_CENTER); + actionsContainer.setPrefWidth(28); + } + private void initializeSelectionBox() { selectionBox.getStyleClass().add(SELECTION_BOX_STYLE_CLASS); HBox.setHgrow(selectionBox, Priority.ALWAYS); @@ -115,24 +132,36 @@ private void initializeSelectionBox() { HBox.setHgrow(labelBox, Priority.ALWAYS); labelBox.setPadding(new Insets(8)); labelBox.setCursor(Cursor.HAND); - - actionsContainer.getChildren().setAll(createCopyButton()); - actionsContainer.setAlignment(Pos.TOP_CENTER); - actionsContainer.setPrefWidth(28); } private Button createCopyButton() { FontIcon copyIcon = FontIcon.of(MaterialDesignC.CONTENT_COPY); - copyIcon.getStyleClass().add("copy-icon"); + copyIcon.getStyleClass().add("action-icon"); Button copyButton = factory.createIconButton(() -> Localization.lang("Copy"), new CopyFieldValueCommand(Globals.prefs, getText())); copyButton.setGraphic(copyIcon); copyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); copyButton.setMaxHeight(Double.MAX_VALUE); + copyButton.visibleProperty().bind(textProperty().isEmpty().not()); return copyButton; } + public Button createOpenLinkButton() { + Node openLinkIcon = IconTheme.JabRefIcons.OPEN_LINK.getGraphicNode(); + openLinkIcon.getStyleClass().add("action-icon"); + + Button openLinkButton = factory.createIconButton(() -> Localization.lang("Open Link"), new OpenExternalLinkAction(getText())); + openLinkButton.setGraphic(openLinkIcon); + openLinkButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + openLinkButton.setMaxHeight(Double.MAX_VALUE); + + isUrl.bind(EasyBind.map(textProperty(), input -> StringUtil.isNotBlank(input) && URLUtil.isURL(input))); + openLinkButton.visibleProperty().bind(isUrl); + + return openLinkButton; + } + private void initializeScrollPane() { HBox.setHgrow(scrollPane, Priority.ALWAYS); scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java new file mode 100644 index 00000000000..6651be0d530 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java @@ -0,0 +1,32 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import java.io.IOException; + +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.desktop.JabRefDesktop; +import org.jabref.model.strings.StringUtil; + +/** + * This action can open an Url and DOI + * */ +public class OpenExternalLinkAction extends SimpleCommand { + private final String urlOrDoi; + + public OpenExternalLinkAction(String urlOrDoi) { + this.urlOrDoi = urlOrDoi; + } + + @Override + public void execute() { + if (StringUtil.isBlank(urlOrDoi)) { + return; + } + + try { + JabRefDesktop.openBrowser(urlOrDoi); + } catch ( + IOException ex) { + // TODO: Do something + } + } +} From 815d415c0f2f7dfc95b0b10cd18ac7cfe50818b8 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sun, 3 Jul 2022 19:55:19 +0100 Subject: [PATCH 081/122] Add the ability to open DOI identifier --- .../newmergedialog/cell/FieldValueCell.java | 3 ++- .../newmergedialog/cell/OpenExternalLinkAction.java | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index acb1cb4d600..9e86ab58e15 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -27,6 +27,7 @@ import org.jabref.gui.fieldeditors.URLUtil; import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.identifier.DOI; import org.jabref.model.strings.StringUtil; import com.tobiasdiez.easybind.EasyBind; @@ -156,7 +157,7 @@ public Button createOpenLinkButton() { openLinkButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); openLinkButton.setMaxHeight(Double.MAX_VALUE); - isUrl.bind(EasyBind.map(textProperty(), input -> StringUtil.isNotBlank(input) && URLUtil.isURL(input))); + isUrl.bind(EasyBind.map(textProperty(), input -> StringUtil.isNotBlank(input) && (URLUtil.isURL(input) || DOI.isValid(input)))); openLinkButton.visibleProperty().bind(isUrl); return openLinkButton; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java index 6651be0d530..102b192458a 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/OpenExternalLinkAction.java @@ -1,9 +1,11 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; import java.io.IOException; +import java.net.URI; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.desktop.JabRefDesktop; +import org.jabref.model.entry.identifier.DOI; import org.jabref.model.strings.StringUtil; /** @@ -23,7 +25,14 @@ public void execute() { } try { - JabRefDesktop.openBrowser(urlOrDoi); + String url; + if (DOI.isValid(urlOrDoi)) { + url = DOI.parse(urlOrDoi).flatMap(DOI::getExternalURI).map(URI::toString).orElse(""); + } else { + url = urlOrDoi; + } + + JabRefDesktop.openBrowser(url); } catch ( IOException ex) { // TODO: Do something From 89357c6ed4a6307d91dfc70b0eeae88d014244d4 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sun, 3 Jul 2022 20:07:02 +0100 Subject: [PATCH 082/122] Cleanup --- .../mergeentries/newmergedialog/cell/FieldValueCell.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 9e86ab58e15..82ca6b0c235 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -3,7 +3,6 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.css.PseudoClass; import javafx.geometry.Insets; @@ -76,8 +75,6 @@ protected void invalidated() { private final HBox selectionBox = new HBox(); private final HBox actionsContainer = new HBox(); - private final BooleanProperty isUrl = new SimpleBooleanProperty(); - public FieldValueCell(String text, int rowIndex) { super(text, rowIndex); initialize(); @@ -157,8 +154,7 @@ public Button createOpenLinkButton() { openLinkButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); openLinkButton.setMaxHeight(Double.MAX_VALUE); - isUrl.bind(EasyBind.map(textProperty(), input -> StringUtil.isNotBlank(input) && (URLUtil.isURL(input) || DOI.isValid(input)))); - openLinkButton.visibleProperty().bind(isUrl); + openLinkButton.visibleProperty().bind(EasyBind.map(textProperty(), input -> StringUtil.isNotBlank(input) && (URLUtil.isURL(input) || DOI.isValid(input)))); return openLinkButton; } From 9973e03d11959f73cd0912ec4fc5f717dd2a60df Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sun, 3 Jul 2022 20:12:01 +0100 Subject: [PATCH 083/122] Checkstyle --- .../jabref/gui/duplicationFinder/DuplicateResolverDialog.java | 1 - .../newmergedialog/diffhighlighter/SplitDiffHighlighter.java | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java index 822c63bf77f..cec54b0d192 100644 --- a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java +++ b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateResolverDialog.java @@ -10,7 +10,6 @@ import org.jabref.gui.StateManager; import org.jabref.gui.duplicationFinder.DuplicateResolverDialog.DuplicateResolverResult; import org.jabref.gui.help.HelpAction; -import org.jabref.gui.mergeentries.MergeEntries; import org.jabref.gui.mergeentries.newmergedialog.ThreeWayMergeView; import org.jabref.gui.util.BaseDialog; import org.jabref.gui.util.DialogWindowState; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java index 5b7275220eb..14570f6b2a8 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/diffhighlighter/SplitDiffHighlighter.java @@ -1,12 +1,9 @@ package org.jabref.gui.mergeentries.newmergedialog.diffhighlighter; -import java.util.ArrayList; import java.util.List; -import java.util.Optional; import com.github.difflib.DiffUtils; import com.github.difflib.patch.AbstractDelta; -import com.github.difflib.patch.DeltaType; import org.fxmisc.richtext.StyleClassedTextArea; /** From ee64212d5bcece7c20a873d0996296cd9b8d59a2 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Mon, 4 Jul 2022 21:29:07 +0100 Subject: [PATCH 084/122] Improve URL validation logic --- src/main/java/org/jabref/gui/fieldeditors/URLUtil.java | 7 ++++++- src/test/java/org/jabref/logic/net/URLUtilTest.java | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java b/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java index bb73b95a6d9..1ee8e8b2652 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java +++ b/src/main/java/org/jabref/gui/fieldeditors/URLUtil.java @@ -74,7 +74,12 @@ public static String cleanGoogleSearchURL(String url) { * @return true if url contains a valid URL */ public static boolean isURL(String url) { - return url.contains("://"); + try { + new URL(url); + return true; + } catch (MalformedURLException e) { + return false; + } } /** diff --git a/src/test/java/org/jabref/logic/net/URLUtilTest.java b/src/test/java/org/jabref/logic/net/URLUtilTest.java index 04ed0e99a94..d69d7a1df7e 100644 --- a/src/test/java/org/jabref/logic/net/URLUtilTest.java +++ b/src/test/java/org/jabref/logic/net/URLUtilTest.java @@ -73,4 +73,9 @@ void isURLshouldRejectInvalidURL() { assertFalse(URLUtil.isURL("www.google.com")); assertFalse(URLUtil.isURL("google.com")); } + + @Test + void isURLshouldRejectEmbeddedURL() { + assertFalse(URLUtil.isURL("dblp computer science bibliography, http://dblp.org")); + } } From 5af4864faf3f42316395acf4007f9df581ca80d6 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Mon, 4 Jul 2022 21:42:18 +0100 Subject: [PATCH 085/122] Use DiffMethod in merge toolbar for consistency --- .../newmergedialog/FieldRowController.java | 6 +-- .../newmergedialog/ShowDiffConfig.java | 8 ++-- .../newmergedialog/ThreeWayMergeView.java | 8 ++-- .../toolbar/ThreeWayMergeToolbar.fxml | 4 +- .../toolbar/ThreeWayMergeToolbar.java | 39 +++++++------------ 5 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 24a949b230e..d1fb3b248a9 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -53,7 +53,7 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, } }); - // Because when both left and right have the same values, then only left value will be shown + // When left and right have the same value, only the left value will be shown if (hasEqualLeftAndRightValues()) { rightValueCell.setDisable(true); } @@ -111,9 +111,9 @@ public void showDiff(ShowDiffConfig diffConfig) { // Clearing old diff styles based on previous diffConfig hideDiff(); if (diffConfig.diffView() == ThreeWayMergeToolbar.DiffView.UNIFIED) { - new UnifiedDiffHighlighter(leftLabel, rightLabel, diffConfig.diffMethod()).highlight(); + new UnifiedDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight(); } else { - new SplitDiffHighlighter(leftLabel, rightLabel, diffConfig.diffMethod()).highlight(); + new SplitDiffHighlighter(leftLabel, rightLabel, diffConfig.diffHighlightingMethod()).highlight(); } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java index 05a4d0121a9..62697e3119b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ShowDiffConfig.java @@ -1,7 +1,9 @@ package org.jabref.gui.mergeentries.newmergedialog; -import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; -import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; +import static org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter.DiffMethod; +import static org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar.DiffView; -public record ShowDiffConfig(ThreeWayMergeToolbar.DiffView diffView, DiffHighlighter.DiffMethod diffMethod) { +public record ShowDiffConfig( + DiffView diffView, + DiffMethod diffHighlightingMethod) { } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index fe77ea972c5..0c6221450f4 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -10,7 +10,6 @@ import javafx.scene.layout.VBox; import javafx.stage.Screen; -import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; @@ -66,13 +65,12 @@ private void initializeToolbar() { toolbar.showDiffProperty().addListener(e -> updateDiff()); toolbar.diffViewProperty().addListener(e -> updateDiff()); - toolbar.diffHighlightModeProperty().addListener(e -> updateDiff()); + toolbar.diffHighlightingMethodProperty().addListener(e -> updateDiff()); } private void updateDiff() { if (toolbar.isShowDiffEnabled()) { - DiffHighlighter.DiffMethod diffMethod = toolbar.getDiffHighlightMode() == ThreeWayMergeToolbar.DiffHighlightMode.WORDS ? DiffHighlighter.DiffMethod.WORDS : DiffHighlighter.DiffMethod.CHARS; - fieldRowControllerList.forEach(fieldRow -> fieldRow.showDiff(new ShowDiffConfig(toolbar.getDiffView(), diffMethod))); + fieldRowControllerList.forEach(fieldRow -> fieldRow.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod()))); } else { fieldRowControllerList.forEach(FieldRowController::hideDiff); } @@ -166,7 +164,7 @@ public void selectRightEntryValues() { public void showDiff(ShowDiffConfig diffConfig) { toolbar.setDiffView(diffConfig.diffView()); - toolbar.setDiffHighlightMode(ThreeWayMergeToolbar.DiffHighlightMode.from(diffConfig.diffMethod())); + toolbar.setDiffHighlightingMethod(diffConfig.diffHighlightingMethod()); toolbar.setShowDiff(true); } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml index 39df997c47b..2361c686981 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml @@ -38,10 +38,10 @@
- + - +
diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java index 5c37e61a837..6efadffa2bf 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java @@ -1,7 +1,6 @@ package org.jabref.gui.mergeentries.newmergedialog.toolbar; import java.util.Arrays; -import java.util.Objects; import javafx.beans.binding.BooleanExpression; import javafx.beans.property.ObjectProperty; @@ -20,6 +19,8 @@ import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBinding; +import static org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter.DiffMethod; + public class ThreeWayMergeToolbar extends AnchorPane { @FXML private RadioButton highlightCharactersRadioButtons; @@ -28,7 +29,7 @@ public class ThreeWayMergeToolbar extends AnchorPane { private RadioButton highlightWordsRadioButton; @FXML - private ToggleGroup diffHighlightModeToggleGroup; + private ToggleGroup diffHighlightingMethodToggleGroup; @FXML private ComboBox diffViewComboBox; @@ -42,7 +43,7 @@ public class ThreeWayMergeToolbar extends AnchorPane { @FXML private Button selectRightEntryValuesButton; - private final ObjectProperty diffHighlightMode = new SimpleObjectProperty<>(); + private final ObjectProperty diffHighlightingMethod = new SimpleObjectProperty<>(); private EasyBinding showDiff; public ThreeWayMergeToolbar() { @@ -87,15 +88,15 @@ public DiffView fromString(String string) { highlightWordsRadioButton.disableProperty().bind(notShowDiffProperty()); highlightCharactersRadioButtons.disableProperty().bind(notShowDiffProperty()); - diffHighlightModeToggleGroup.selectedToggleProperty().addListener((observable -> { - if (diffHighlightModeToggleGroup.getSelectedToggle().equals(highlightCharactersRadioButtons)) { - diffHighlightMode.set(DiffHighlightMode.CHARS); + diffHighlightingMethodToggleGroup.selectedToggleProperty().addListener((observable -> { + if (diffHighlightingMethodToggleGroup.getSelectedToggle().equals(highlightCharactersRadioButtons)) { + diffHighlightingMethod.set(DiffMethod.CHARS); } else { - diffHighlightMode.set(DiffHighlightMode.WORDS); + diffHighlightingMethod.set(DiffMethod.WORDS); } })); - diffHighlightModeToggleGroup.selectToggle(highlightWordsRadioButton); + diffHighlightingMethodToggleGroup.selectToggle(highlightWordsRadioButton); } public ObjectProperty diffViewProperty() { @@ -134,16 +135,16 @@ public Boolean isShowDiffEnabled() { return showDiffProperty().get(); } - public ObjectProperty diffHighlightModeProperty() { - return diffHighlightMode; + public ObjectProperty diffHighlightingMethodProperty() { + return diffHighlightingMethod; } - public DiffHighlightMode getDiffHighlightMode() { - return diffHighlightModeProperty().get(); + public DiffMethod getDiffHighlightingMethod() { + return diffHighlightingMethodProperty().get(); } - public void setDiffHighlightMode(DiffHighlightMode diffHighlightMode) { - diffHighlightModeProperty().set(diffHighlightMode); + public void setDiffHighlightingMethod(DiffMethod diffHighlightingMethod) { + diffHighlightingMethodProperty().set(diffHighlightingMethod); } public void setOnSelectLeftEntryValuesButtonClicked(Runnable onClick) { @@ -195,14 +196,4 @@ public static DiffView fromString(String str) { .orElseThrow(IllegalArgumentException::new); } } - - // TODO: remove this and use DiffMethod - public enum DiffHighlightMode { - WORDS, CHARS; - - public static DiffHighlightMode from(DiffHighlighter.DiffMethod diffMethod) { - Objects.requireNonNull(diffMethod, "Diff method is required"); - return diffMethod == DiffHighlighter.DiffMethod.WORDS ? WORDS : CHARS; - } - } } From ed2a0acb84a0c55482cef472c71d64149c7e16ec Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 00:15:11 +0100 Subject: [PATCH 086/122] i18n --- src/main/resources/l10n/JabRef_en.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index e66b587e6a1..9f1625d21a6 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2506,3 +2506,5 @@ Move\ value=Move value Swap\ values=Swap values Copy\ or\ move\ the\ value\ of\ one\ field\ to\ another=Copy or move the value of one field to another Automatic\ field\ editor=Automatic field editor + +Open\ Link=Open Link From bcec6a85484bc59cda2bc5a0c8dfdd2a0eca77e5 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 00:19:42 +0100 Subject: [PATCH 087/122] Cleanup css --- src/main/java/org/jabref/gui/Base.css | 3 +-- src/main/java/org/jabref/gui/Dark.css | 2 +- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.css | 7 ------- .../gui/mergeentries/newmergedialog/cell/HeaderCell.java | 2 +- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 98b8fc4df8e..550796b5ac8 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -1,7 +1,6 @@ .root { - -jr-transparent-green: rgba(16, 172, 132, 0.12); -jr-row-odd-background: -fx-control-inner-background-alt; -jr-row-even-background: -fx-control-inner-background; /* @@ -647,7 +646,7 @@ TextFlow > .tooltip-text-monospaced { } -.header-cell { +.merge-header-cell { -fx-border-width: 0 0 1 0; -fx-border-color: -jr-gray-1; -fx-background-color: -jr-row-even-background; diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 21ec0ac6b57..55405297abd 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -109,7 +109,7 @@ -fx-opacity: 100%; } -.header-cell { +.merge-header-cell { -fx-border-width: 0 0 1 0; -fx-border-color: -fx-outer-border; -fx-background-color: -jr-row-odd-background; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index 59963812688..5686b37d855 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -1,8 +1,3 @@ -.merged-field { -/* -fx-border-color: #dfdfdf; - -fx-border-width: 0 0 0 1.5;*/ -} - .merged-field .styled-text-area { -fx-background-color: -fx-outer-border, -fx-control-inner-background; -fx-background-insets: 0, 1; @@ -16,8 +11,6 @@ } .field-value:selected .selection-box { - /* -fx-background-color: -jr-accent-alt; - -fx-border-color: -jr-accent-alt;*/ -fx-background-color: -jr-accent-alt; -fx-border-color: -jr-accent; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java index 5a7c6a9ff71..2fd82afd38b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/HeaderCell.java @@ -4,7 +4,7 @@ import javafx.scene.control.Label; public class HeaderCell extends AbstractCell { - public static final String DEFAULT_STYLE_CLASS = "header-cell"; + public static final String DEFAULT_STYLE_CLASS = "merge-header-cell"; private final Label label = new Label(); public HeaderCell(String text) { From be0a7fbf620bebd7754e722b4237e9a96b2aee35 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 00:35:00 +0100 Subject: [PATCH 088/122] Remove redundant icon style --- src/main/java/org/jabref/gui/Base.css | 8 ++------ src/main/java/org/jabref/gui/Dark.css | 16 +++------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 550796b5ac8..09262fc587e 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -632,20 +632,16 @@ TextFlow > .tooltip-text-monospaced { } .field-value .action-icon { + -fx-blend-mode: multiply; + -fx-opacity: 69%; -fx-icon-size: 14; -fx-icon-color: -fx-text-background-color; - -fx-blend-mode: multiply ; - -fx-opacity: 69%; } .field-value:disabled .action-icon { -fx-opacity: 0%; } -.field-value:selected .action-icon { - -} - .merge-header-cell { -fx-border-width: 0 0 1 0; -fx-border-color: -jr-gray-1; diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 55405297abd..4dd3e5eb65f 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -93,20 +93,10 @@ .tree-table-row-cell:hover { -fx-background-color: -jr-hover; } -.field-value .action-icon { - -fx-icon-size: 14; - -fx-icon-color: -fx-text-background-color; - -fx-blend-mode: inherit; - -fx-opacity: 100%; -} -.field-value:disabled .action-icon { - -fx-opacity: 0%; -} - -.field-value:selected .action-icon { - -fx-icon-color: -fx-text-background-color; - -fx-opacity: 100%; +.field-value .action-icon { + -fx-blend-mode: none; + -fx-opacity: 90%; } .merge-header-cell { From 0602237e442127bd684e06da94556d71d53bf80f Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 00:38:22 +0100 Subject: [PATCH 089/122] Rename style class field-value to merge-field-value - Since it is used in Base.css and Dark.css, it is critical to distinguish the field cell used is related to the merge UI. --- src/main/java/org/jabref/gui/Base.css | 4 ++-- src/main/java/org/jabref/gui/Dark.css | 2 +- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.css | 4 ++-- .../gui/mergeentries/newmergedialog/cell/FieldValueCell.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 09262fc587e..aad81750edd 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -631,14 +631,14 @@ TextFlow > .tooltip-text-monospaced { -fx-background-insets: 0; } -.field-value .action-icon { +.merge-field-value .action-icon { -fx-blend-mode: multiply; -fx-opacity: 69%; -fx-icon-size: 14; -fx-icon-color: -fx-text-background-color; } -.field-value:disabled .action-icon { +.merge-field-value:disabled .action-icon { -fx-opacity: 0%; } diff --git a/src/main/java/org/jabref/gui/Dark.css b/src/main/java/org/jabref/gui/Dark.css index 4dd3e5eb65f..3271900cf05 100644 --- a/src/main/java/org/jabref/gui/Dark.css +++ b/src/main/java/org/jabref/gui/Dark.css @@ -94,7 +94,7 @@ -fx-background-color: -jr-hover; } -.field-value .action-icon { +.merge-field-value .action-icon { -fx-blend-mode: none; -fx-opacity: 90%; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index 5686b37d855..fbfdf859411 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -10,12 +10,12 @@ -fx-highlight-text-fill: -fx-text-inner-color; } -.field-value:selected .selection-box { +.merge-field-value:selected .selection-box { -fx-background-color: -jr-accent-alt; -fx-border-color: -jr-accent; } -.field-value .selection-box { +.merge-field-value .selection-box { -fx-background-color: #0000; -fx-border-color: #0000; -fx-border-radius: 8; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java index 82ca6b0c235..f31639ccc25 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldValueCell.java @@ -43,7 +43,7 @@ public class FieldValueCell extends AbstractCell implements Toggle { public static final Logger LOGGER = LoggerFactory.getLogger(FieldValueCell.class); - public static final String DEFAULT_STYLE_CLASS = "field-value"; + public static final String DEFAULT_STYLE_CLASS = "merge-field-value"; public static final String SELECTION_BOX_STYLE_CLASS = "selection-box"; private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected"); From 41cae265bb8f70241d1c9c9298ac750987b1b5c3 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 00:43:17 +0100 Subject: [PATCH 090/122] Remove old Merge entries UI --- .../jabref/gui/mergeentries/MergeEntries.css | 12 - .../jabref/gui/mergeentries/MergeEntries.java | 417 ------------------ 2 files changed, 429 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/mergeentries/MergeEntries.css delete mode 100644 src/main/java/org/jabref/gui/mergeentries/MergeEntries.java diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntries.css b/src/main/java/org/jabref/gui/mergeentries/MergeEntries.css deleted file mode 100644 index 82f5ca6d3ee..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntries.css +++ /dev/null @@ -1,12 +0,0 @@ -.text-changed { - -fx-fill: darkgreen; -} - -.text-added { - -fx-fill: #54A3F2; -} - -.text-removed { - -fx-fill: #FA5B68; -} - diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntries.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntries.java deleted file mode 100644 index 99472efdd89..00000000000 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntries.java +++ /dev/null @@ -1,417 +0,0 @@ -package org.jabref.gui.mergeentries; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - -import javafx.collections.FXCollections; -import javafx.geometry.HPos; -import javafx.geometry.Insets; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.RadioButton; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.ToggleGroup; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.text.Text; -import javafx.scene.text.TextFlow; -import javafx.stage.Screen; - -import org.jabref.gui.Globals; -import org.jabref.gui.icon.IconTheme.JabRefIcons; -import org.jabref.gui.util.ViewModelListCellFactory; -import org.jabref.gui.util.component.DiffHighlightingTextPane; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.field.FieldFactory; - -import com.tobiasdiez.easybind.EasyBind; - -public class MergeEntries extends BorderPane { - - private static final int NUMBER_OF_COLUMNS = 6; - private static final int LEFT_RADIOBUTTON_INDEX = 0; - private static final int RIGHT_RADIOBUTTON_INDEX = 2; - private final ComboBox diffMode = new ComboBox<>(); - - // Headings - private final List columnHeadings = Arrays.asList( - Localization.lang("Field"), - Localization.lang("Left entry"), - "left icon", - Localization.lang("None"), - "right icon", - Localization.lang("Right entry")); - private final Set identicalFields = new HashSet<>(); - private final Set differentFields = new HashSet<>(); - private final BibEntry mergedEntry = new BibEntry(); - private final BibEntry leftEntry; - private final BibEntry rightEntry; - private final Map leftTextPanes = new HashMap<>(); - private final Set allFields = new TreeSet<>(Comparator.comparing(Field::getName)); - private final Map rightTextPanes = new HashMap<>(); - private final Map> radioButtons = new HashMap<>(); - private Boolean identicalTypes; - private List typeRadioButtons; - private final DefaultRadioButtonSelectionMode defaultRadioButtonSelectionMode; - private final List leftRadioButtons = new ArrayList<>(); - private final List rightRadioButtons = new ArrayList<>(); - - /** - * Constructor with optional column captions for the two entries - * - * @param entryLeft Left entry - * @param entryRight Right entry - * @param headingLeft Heading for left entry - * @param headingRight Heading for right entry - * @param defaultRadioButtonSelectionMode If the left or the right side of the radio button should be preselected - */ - public MergeEntries(BibEntry entryLeft, BibEntry entryRight, String headingLeft, String headingRight, DefaultRadioButtonSelectionMode defaultRadioButtonSelectionMode) { - this.leftEntry = entryLeft; - this.rightEntry = entryRight; - this.defaultRadioButtonSelectionMode = defaultRadioButtonSelectionMode; - - initialize(); - setLeftHeaderText(headingLeft); - setRightHeaderText(headingRight); - } - - /** - * Constructor with optional column captions for the two entries - * - * @param entryLeft Left entry - * @param entryRight Right entry - * @param headingLeft Heading for left entry - * @param headingRight Heading for right entry - */ - public MergeEntries(BibEntry entryLeft, BibEntry entryRight, String headingLeft, String headingRight) { - this(entryLeft, entryRight, headingLeft, headingRight, DefaultRadioButtonSelectionMode.LEFT); - } - - /** - * Constructor taking two entries - * - * @param entryLeft Left entry - * @param entryRight Right entry - * @param defaultRadioButtonSelectionMode If the left or the right side of the radio button should be preselected - */ - public MergeEntries(BibEntry entryLeft, BibEntry entryRight, DefaultRadioButtonSelectionMode defaultRadioButtonSelectionMode) { - leftEntry = entryLeft; - rightEntry = entryRight; - this.defaultRadioButtonSelectionMode = defaultRadioButtonSelectionMode; - initialize(); - } - - /** - * Constructor taking two entries - * - * @param entryLeft Left entry - * @param entryRight Right entry - */ - public MergeEntries(BibEntry entryLeft, BibEntry entryRight) { - this(entryLeft, entryRight, DefaultRadioButtonSelectionMode.LEFT); - } - - private static String getDisplayText(DiffMode mode) { - return switch (mode) { - case PLAIN -> Localization.lang("Plain text"); - case WORD -> Localization.lang("Show diff") + " - " + Localization.lang("word"); - case CHARACTER -> Localization.lang("Show diff") + " - " + Localization.lang("character"); - case WORD_SYMMETRIC -> Localization.lang("Show symmetric diff") + " - " + Localization.lang("word"); - case CHARACTER_SYMMETRIC -> Localization.lang("Show symmetric diff") + " - " + Localization.lang("character"); - }; - } - - /** - * Main function for building the merge entry JPanel - */ - private void initialize() { - this.setPrefHeight(Screen.getPrimary().getBounds().getHeight() * 0.75); - this.setPrefWidth(Screen.getPrimary().getBounds().getWidth() * 0.75); - - setupFields(); - - fillDiffModes(); - - GridPane mergePanel = new GridPane(); - mergePanel.setVgap(10); - mergePanel.setHgap(15); - ColumnConstraints columnLabel = new ColumnConstraints(); - columnLabel.setHgrow(Priority.ALWAYS); - ColumnConstraints columnValues = new ColumnConstraints(); - columnValues.setHgrow(Priority.NEVER); - columnValues.setPercentWidth(40); - ColumnConstraints columnSelect = new ColumnConstraints(); - columnSelect.setHgrow(Priority.NEVER); - columnSelect.setHalignment(HPos.CENTER); - // See columnHeadings variable for the headings: 1) field, 2) left content, 3) left arrow, 4) "none", 5) right arrow, 6) right content - mergePanel.getColumnConstraints().setAll(columnLabel, columnValues, columnSelect, columnSelect, columnSelect, columnValues); - - setupHeadingRows(mergePanel); - setupEntryTypeRow(mergePanel); - setupFieldRows(mergePanel); - - ScrollPane scrollPane = new ScrollPane(mergePanel); - scrollPane.setFitToWidth(true); - setCenter(scrollPane); - - updateFieldValues(allFields); - - updateMergedEntry(); - - getStylesheets().add(0, MergeEntries.class.getResource("MergeEntries.css").toExternalForm()); - } - - private void setupFieldRows(GridPane mergePanel) { - // For all fields in joint add a row and possibly radio buttons - int row = 2; - for (Field field : allFields) { - Label label = new Label(field.getDisplayName()); - label.setMinWidth(USE_PREF_SIZE); - mergePanel.add(label, 0, row); - Optional leftString = leftEntry.getField(field); - Optional rightString = rightEntry.getField(field); - if (leftString.equals(rightString)) { - identicalFields.add(field); - } else { - differentFields.add(field); - } - - // Left text pane - if (leftString.isPresent()) { - TextFlow tf = new DiffHighlightingTextPane(); - mergePanel.add(tf, 1, row); - leftTextPanes.put(field, tf); - } - - // Add radio buttons if the two entries do not have identical fields - if (identicalFields.contains(field)) { - mergedEntry.setField(field, leftString.get()); // Will only happen if both entries have the field and the content is identical - } else { - ToggleGroup group = new ToggleGroup(); - List list = new ArrayList<>(3); - for (int k = 0; k < 3; k++) { - RadioButton button = new RadioButton(); - EasyBind.subscribe(button.selectedProperty(), selected -> updateMergedEntry()); - group.getToggles().add(button); - mergePanel.add(button, 2 + k, row); - list.add(button); - } - radioButtons.put(field, list); - if (leftString.isPresent()) { - leftRadioButtons.add(list.get(LEFT_RADIOBUTTON_INDEX)); - list.get(LEFT_RADIOBUTTON_INDEX).setSelected(true); - if (rightString.isEmpty()) { - list.get(RIGHT_RADIOBUTTON_INDEX).setDisable(true); - } else if (this.defaultRadioButtonSelectionMode == DefaultRadioButtonSelectionMode.RIGHT) { - list.get(RIGHT_RADIOBUTTON_INDEX).setSelected(true); - rightRadioButtons.add(list.get(RIGHT_RADIOBUTTON_INDEX)); - } else { - rightRadioButtons.add(list.get(RIGHT_RADIOBUTTON_INDEX)); - } - } else { - list.get(LEFT_RADIOBUTTON_INDEX).setDisable(true); - list.get(RIGHT_RADIOBUTTON_INDEX).setSelected(true); - rightRadioButtons.add(list.get(RIGHT_RADIOBUTTON_INDEX)); - } - } - - // Right text pane - if (rightString.isPresent()) { - TextFlow tf = new DiffHighlightingTextPane(); - mergePanel.add(tf, 5, row); - rightTextPanes.put(field, tf); - } - row++; - } - } - - private void setupEntryTypeRow(GridPane mergePanel) { - // Start with entry type - int rowIndex = 1; - mergePanel.add(new Label(Localization.lang("Entry type")), 0, rowIndex); - if (leftEntry.getType().equals(rightEntry.getType())) { - mergePanel.add(DiffHighlighting.forUnchanged(leftEntry.getType().getDisplayName()), 1, rowIndex); - mergePanel.add(DiffHighlighting.forUnchanged(rightEntry.getType().getDisplayName()), 5, rowIndex); - identicalTypes = true; - } else { - mergePanel.add(DiffHighlighting.forChanged(leftEntry.getType().getDisplayName()), 1, rowIndex); - mergePanel.add(DiffHighlighting.forChanged(rightEntry.getType().getDisplayName()), 5, rowIndex); - identicalTypes = false; - ToggleGroup group = new ToggleGroup(); - typeRadioButtons = new ArrayList<>(2); - - for (int k = 0; k < 3; k += 2) { - RadioButton button = new RadioButton(); - EasyBind.subscribe(button.selectedProperty(), selected -> updateMergedEntry()); - typeRadioButtons.add(button); - group.getToggles().add(button); - mergePanel.add(button, 2 + k, rowIndex); - } - if (defaultRadioButtonSelectionMode == DefaultRadioButtonSelectionMode.RIGHT) { - typeRadioButtons.get(1).setSelected(true); // This Radio Button list does not have a third option as compared to the fields, so do not use the constants here - rightRadioButtons.add(typeRadioButtons.get(1)); - } else { - typeRadioButtons.get(0).setSelected(true); - leftRadioButtons.add(typeRadioButtons.get(0)); - } - } - } - - private void setupHeadingRows(GridPane mergePanel) { - // Set headings - for (int i = 0; i < NUMBER_OF_COLUMNS; i++) { - if (i == 2) { - Button selectAllLeft = new Button(); - selectAllLeft.setGraphic(JabRefIcons.LEFT.getGraphicNode()); - selectAllLeft.setOnAction(evt -> this.selectAllLeftRadioButtons()); - selectAllLeft.setTooltip(new Tooltip(Localization.lang("Select all changes on the left"))); - mergePanel.add(selectAllLeft, i, 0); - } else if (i == 4) { - Button selectAllRight = new Button(); - selectAllRight.setOnAction(evt -> this.selectAllRightRadioButtons()); - selectAllRight.setGraphic(JabRefIcons.RIGHT.getGraphicNode()); - selectAllRight.setTooltip(new Tooltip(Localization.lang("Select all changes on the right"))); - mergePanel.add(selectAllRight, i, 0); - } else { - Label colHeading = new Label(columnHeadings.get(i)); - colHeading.setMinWidth(USE_PREF_SIZE); - mergePanel.add(colHeading, i, 0); - } - } - } - - private void fillDiffModes() { - diffMode.setItems(FXCollections.observableList(Arrays.asList(DiffMode.values()))); - new ViewModelListCellFactory() - .withText(MergeEntries::getDisplayText) - .install(diffMode); - DiffMode diffModePref = Globals.prefs.getGuiPreferences().getMergeDiffMode(); - diffMode.setValue(diffModePref); - EasyBind.subscribe(this.diffMode.valueProperty(), mode -> { - updateFieldValues(differentFields); - Globals.prefs.getGuiPreferences().setMergeDiffMode(mode); - }); - - HBox heading = new HBox(10); - heading.getChildren().setAll(this.diffMode); - setTop(heading); - BorderPane.setMargin(heading, new Insets(0, 0, 10, 0)); - } - - private void setupFields() { - allFields.addAll(leftEntry.getFields()); - allFields.addAll(rightEntry.getFields()); - - // Do not show internal fields - Set internalFields = allFields.stream().filter(FieldFactory::isInternalField).collect(Collectors.toSet()); - allFields.removeAll(internalFields); - } - - private void updateFieldValues(Collection fields) { - for (Field field : fields) { - String leftString = leftEntry.getField(field).orElse(""); - String rightString = rightEntry.getField(field).orElse(""); - List leftText = leftString.isEmpty() ? Collections.emptyList() : Collections.singletonList(DiffHighlighting.forUnchanged(leftString)); - List rightText = rightString.isEmpty() ? Collections.emptyList() : Collections.singletonList(DiffHighlighting.forUnchanged(rightString)); - switch (diffMode.getValue()) { - case PLAIN: - break; - case WORD: - rightText = DiffHighlighting.generateDiffHighlighting(leftString, rightString, " "); - break; - case CHARACTER: - rightText = DiffHighlighting.generateDiffHighlighting(leftString, rightString, ""); - break; - case WORD_SYMMETRIC: - leftText = DiffHighlighting.generateSymmetricHighlighting(leftString, rightString, " "); - rightText = DiffHighlighting.generateSymmetricHighlighting(rightString, leftString, " "); - break; - case CHARACTER_SYMMETRIC: - leftText = DiffHighlighting.generateSymmetricHighlighting(leftString, rightString, ""); - rightText = DiffHighlighting.generateSymmetricHighlighting(rightString, leftString, ""); - break; - default: - throw new UnsupportedOperationException("Not implemented " + diffMode.getValue()); - } - if (!leftText.isEmpty() && leftTextPanes.containsKey(field)) { - leftTextPanes.get(field).getChildren().setAll(leftText); - } - if (!rightText.isEmpty() && rightTextPanes.containsKey(field)) { - rightTextPanes.get(field).getChildren().setAll(rightText); - } - } - } - - public void selectAllRightRadioButtons() { - for (RadioButton radioButton : rightRadioButtons) { - radioButton.setSelected(true); - } - } - - public void selectAllLeftRadioButtons() { - for (RadioButton radioButton : leftRadioButtons) { - radioButton.setSelected(true); - } - } - - public BibEntry getMergedEntry() { - return mergedEntry; - } - - private void updateMergedEntry() { - // Check if the type has changed - if (!identicalTypes && !typeRadioButtons.isEmpty() && typeRadioButtons.get(0).isSelected()) { - mergedEntry.setType(leftEntry.getType()); - } else { - mergedEntry.setType(rightEntry.getType()); - } - - // Check the potentially different fields - for (Field field : differentFields) { - if (!radioButtons.containsKey(field)) { - // May happen during initialization -> just ignore - continue; - } - if (radioButtons.get(field).get(LEFT_RADIOBUTTON_INDEX).isSelected()) { - mergedEntry.setField(field, leftEntry.getField(field).get()); // Will only happen if field exists - } else if (radioButtons.get(field).get(RIGHT_RADIOBUTTON_INDEX).isSelected()) { - mergedEntry.setField(field, rightEntry.getField(field).get()); // Will only happen if field exists - } else { - mergedEntry.clearField(field); - } - } - } - - public void setLeftHeaderText(String leftHeaderText) { - columnHeadings.set(1, leftHeaderText); - initialize(); - } - - public void setRightHeaderText(String rightHeaderText) { - columnHeadings.set(5, rightHeaderText); - initialize(); - } - - public enum DefaultRadioButtonSelectionMode { - LEFT, - RIGHT - } -} From 6859ca4bc33ed002335a49579e0976cabaf07a04 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 00:56:28 +0100 Subject: [PATCH 091/122] Fix missing header styles --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index fbfdf859411..416b55b308a 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -39,7 +39,7 @@ -fx-background-color: -jr-menu-background; } -.header-cell .label{ +.merge-header-cell .label{ -fx-font-weight: bold; -fx-padding: 1, 0, 1, 0; } From 5df2d50e4131041cdae82a9cd47b12edbc949917 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Tue, 5 Jul 2022 02:39:00 +0100 Subject: [PATCH 092/122] i18n --- .../ThreeWayMergeHeaderView.java | 3 ++- .../newmergedialog/ThreeWayMergeView.java | 5 +++-- .../toolbar/ThreeWayMergeToolbar.fxml | 6 +++--- .../toolbar/ThreeWayMergeToolbar.java | 7 ++++--- src/main/resources/l10n/JabRef_en.properties | 18 +++++++++--------- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java index a3b33e99461..bf896f4b8a5 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeHeaderView.java @@ -5,6 +5,7 @@ import javafx.scene.layout.GridPane; import org.jabref.gui.mergeentries.newmergedialog.cell.HeaderCell; +import org.jabref.logic.l10n.Localization; /** * GridPane was used instead of a Hbox because Hbox allocates more space for cells @@ -22,7 +23,7 @@ public ThreeWayMergeHeaderView(String leftHeader, String rightHeader) { this.leftHeaderCell = new HeaderCell(leftHeader); this.rightHeaderCell = new HeaderCell(rightHeader); - this.mergedHeaderCell = new HeaderCell("Merged Entry"); + this.mergedHeaderCell = new HeaderCell(Localization.lang("Merged Entry")); addRow(0, new HeaderCell(""), diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 0c6221450f4..7ab42c83d2f 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -11,6 +11,7 @@ import javafx.stage.Screen; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; +import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.InternalField; @@ -19,8 +20,8 @@ public class ThreeWayMergeView extends VBox { public static final int GRID_COLUMN_MIN_WIDTH = 250; - public static final String LEFT_DEFAULT_HEADER = "Left Entry"; - public static final String RIGHT_DEFAULT_HEADER = "Right Entry"; + public static final String LEFT_DEFAULT_HEADER = Localization.lang("Left Entry"); + public static final String RIGHT_DEFAULT_HEADER = Localization.lang("Right Entry"); private final ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); private final ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml index 2361c686981..59fde5d824b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.fxml @@ -30,18 +30,18 @@
- + - + - + diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java index 6efadffa2bf..08189a7647b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/toolbar/ThreeWayMergeToolbar.java @@ -14,6 +14,7 @@ import javafx.util.StringConverter; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.DiffHighlighter; +import org.jabref.logic.l10n.Localization; import com.airhacks.afterburner.views.ViewLoader; import com.tobiasdiez.easybind.EasyBind; @@ -156,7 +157,7 @@ public void setOnSelectRightEntryValuesButtonClicked(Runnable onClick) { } public enum PlainTextOrDiff { - PLAIN_TEXT("Plain Text"), Diff("Show Diff"); + PLAIN_TEXT(Localization.lang("Plain Text")), Diff(Localization.lang("Show Diff")); private final String value; @@ -177,8 +178,8 @@ public static PlainTextOrDiff fromString(String str) { } public enum DiffView { - UNIFIED("Unified View"), - SPLIT("Split View"); + UNIFIED(Localization.lang("Unified View")), + SPLIT(Localization.lang("Split View")); private final String value; DiffView(String value) { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 0e7b47e2982..d29620e12c4 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -323,7 +323,6 @@ Entry\ preview=Entry preview Entry\ table=Entry table Entry\ table\ columns=Entry table columns Entry\ Title\ (Required\ to\ deliver\ recommendations.)=Entry Title (Required to deliver recommendations.) -Entry\ type=Entry type Error=Error Error\ occurred\ when\ parsing\ entry=Error occurred when parsing entry Error\ opening\ file=Error opening file @@ -1306,8 +1305,8 @@ Help\ on\ Name\ Formatting=Help on Name Formatting Add\ new\ file\ type=Add new file type -Left\ entry=Left entry -Right\ entry=Right entry +Left\ Entry=Left Entry +Right\ Entry=Right Entry Original\ entry=Original entry No\ information\ added=No information added Select\ at\ least\ one\ entry\ to\ manage\ keywords.=Select at least one entry to manage keywords. @@ -1552,11 +1551,7 @@ Verse=Verse change\ entries\ of\ group=change entries of group odd\ number\ of\ unescaped\ '\#'=odd number of unescaped '#' -Plain\ text=Plain text Show\ diff=Show diff -character=character -word=word -Show\ symmetric\ diff=Show symmetric diff Copy\ Version=Copy Version Maintainers=Maintainers Contributors=Contributors @@ -2081,8 +2076,6 @@ Linked\ identifiers=Linked identifiers insert\ entries=insert entries In\ JabRef=In JabRef On\ disk=On disk -Select\ all\ changes\ on\ the\ left=Select all changes on the left -Select\ all\ changes\ on\ the\ right=Select all changes on the right Dismiss=Dismiss Mark\ all\ changes\ as\ accepted=Mark all changes as accepted Unmark\ all\ changes=Unmark all changes @@ -2508,6 +2501,13 @@ Copy\ or\ move\ the\ value\ of\ one\ field\ to\ another=Copy or move the value o Automatic\ field\ editor=Automatic field editor Open\ Link=Open Link +Highlight\ words=Highlight words +Highlight\ characters=Highlight characters +Unified\ View=Unified View +Split\ View=Split View +Plain\ Text=Plain Text +Show\ Diff=Show Diff +Merged\ Entry=Merged Entry (Note\:\ If\ original\ entries\ lack\ keywords\ to\ qualify\ for\ the\ new\ group\ configuration,\ confirming\ here\ will\ add\ them)=(Note: If original entries lack keywords to qualify for the new group configuration, confirming here will add them) Assign=Assign From db706a81e48ac86a695163f8e01094f0ad2baa11 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Wed, 6 Jul 2022 02:08:39 +0100 Subject: [PATCH 093/122] Garbage collect right cell when it's invisible - When both the left and right cells have the same value, only the left value is displayed, making it unnecessary to keep allocating memory for the right cell. --- .../newmergedialog/FieldRowController.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index d1fb3b248a9..38d02467e13 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -16,7 +16,7 @@ public class FieldRowController { private final FieldNameCell fieldNameCell; private final FieldValueCell leftValueCell; - private final FieldValueCell rightValueCell; + private FieldValueCell rightValueCell; private final MergedFieldCell mergedValueCell; private final String leftValue; @@ -53,9 +53,11 @@ public FieldRowController(String fieldName, String leftValue, String rightValue, } }); - // When left and right have the same value, only the left value will be shown + // When both the left and right cells have the same value, only the left value is displayed, + // making it unnecessary to keep allocating memory for the right cell. if (hasEqualLeftAndRightValues()) { - rightValueCell.setDisable(true); + // Setting this to null so the GC release the memory allocated to the right cell. + this.rightValueCell = null; } } @@ -64,7 +66,7 @@ public void selectLeftValue() { } public void selectRightValue() { - if (rightValueCell.isDisabled()) { + if (rightValueCell == null) { selectLeftValue(); } else { toggleGroup.selectToggle(rightValueCell); @@ -96,13 +98,13 @@ public MergedFieldCell getMergedValueCell() { } public boolean hasEqualLeftAndRightValues() { - return !StringUtil.isNullOrEmpty(leftValueCell.getText()) && + return isRightValueCellHidden() || (!StringUtil.isNullOrEmpty(leftValueCell.getText()) && !StringUtil.isNullOrEmpty(rightValueCell.getText()) && - leftValueCell.getText().equals(rightValueCell.getText()); + leftValueCell.getText().equals(rightValueCell.getText())); } public void showDiff(ShowDiffConfig diffConfig) { - if (leftValueCell.isDisabled() || rightValueCell.isDisabled()) { + if (isRightValueCellHidden()) { return; } @@ -118,6 +120,10 @@ public void showDiff(ShowDiffConfig diffConfig) { } public void hideDiff() { + if (isRightValueCellHidden()) { + return; + } + int leftValueLength = getLeftValueCell().getStyleClassedLabel().getLength(); getLeftValueCell().getStyleClassedLabel().clearStyle(0, leftValueLength); getLeftValueCell().getStyleClassedLabel().replaceText(leftValue); @@ -126,4 +132,8 @@ public void hideDiff() { getRightValueCell().getStyleClassedLabel().clearStyle(0, rightValueLength); getRightValueCell().getStyleClassedLabel().replaceText(rightValue); } + + private boolean isRightValueCellHidden() { + return rightValueCell == null; + } } From fffc5d1ad2ceac2ab7dd579cdb1ffd9892f6a96d Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Wed, 6 Jul 2022 02:12:15 +0100 Subject: [PATCH 094/122] Fix typo --- .../gui/mergeentries/newmergedialog/FieldRowController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java index 38d02467e13..8f2411d89cf 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java @@ -66,7 +66,7 @@ public void selectLeftValue() { } public void selectRightValue() { - if (rightValueCell == null) { + if (isRightValueCellHidden()) { selectLeftValue(); } else { toggleGroup.selectToggle(rightValueCell); From b4827ecf294c51b26309e3f86e6fd909f7198381 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:55:26 +0100 Subject: [PATCH 095/122] Create GroupsFieldNameCell --- .../newmergedialog/cell/FieldNameCell.java | 2 ++ .../cell/GroupsFieldNameCell.java | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index 7ad435d59b1..93b0b20d84e 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -2,6 +2,8 @@ import javafx.scene.control.Label; +import org.jabref.gui.actions.Action; + /** * A non-editable cell that contains the name of some field */ diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java new file mode 100644 index 00000000000..b20c1ffcb11 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java @@ -0,0 +1,33 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +public class GroupsFieldNameCell extends FieldNameCell { + private Runnable onMergeGroups; + private Runnable onUnmergeGroups; + private boolean isMerged = false; + + public GroupsFieldNameCell(String text, int rowIndex, boolean groupsMerged) { + super(text, rowIndex); + isMerged = groupsMerged; + setOnMouseClicked(e -> { + if (isMerged) { + if (onUnmergeGroups != null) { + onUnmergeGroups.run(); + isMerged = false; + } + } else { + if (onMergeGroups != null) { + onMergeGroups.run(); + isMerged = true; + } + } + }); + } + + public void setOnMergeGroups(Runnable mergeGroups) { + this.onMergeGroups = mergeGroups; + } + + public void setOnUnmergeGroups(Runnable unmergeGroups) { + this.onUnmergeGroups = unmergeGroups; + } +} From a94acd4c597c3318bdbf525daecb6652c27e1a89 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:56:21 +0100 Subject: [PATCH 096/122] Create MergedGroups to record the state of the groups before and after the merge --- .../gui/mergeentries/newmergedialog/MergedGroups.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/MergedGroups.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/MergedGroups.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/MergedGroups.java new file mode 100644 index 00000000000..f2b1e03da70 --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/MergedGroups.java @@ -0,0 +1,6 @@ +package org.jabref.gui.mergeentries.newmergedialog; + +public record MergedGroups( + String leftEntryGroups, String rightEntryGroups, String mergedGroups +) { +} From 484a3685d975d2d54c350922f7bf7a6bb1bf2a1e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:57:26 +0100 Subject: [PATCH 097/122] Create a factory class for creating field name cells --- ...ldRowController.java => ThreeFieldValues.java} | 11 ++++++++--- .../newmergedialog/cell/FieldNameCellFactory.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) rename src/main/java/org/jabref/gui/mergeentries/newmergedialog/{FieldRowController.java => ThreeFieldValues.java} (92%) create mode 100644 src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java similarity index 92% rename from src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java rename to src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java index 8f2411d89cf..45f34afe920 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/FieldRowController.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java @@ -4,16 +4,18 @@ import javafx.scene.control.ToggleGroup; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCellFactory; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.SplitDiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.UnifiedDiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; +import org.jabref.model.entry.field.Field; import org.jabref.model.strings.StringUtil; import org.fxmisc.richtext.StyleClassedTextArea; -public class FieldRowController { +public class ThreeFieldValues { private final FieldNameCell fieldNameCell; private final FieldValueCell leftValueCell; private FieldValueCell rightValueCell; @@ -23,16 +25,19 @@ public class FieldRowController { private final String rightValue; + private final String fieldName; + private final ToggleGroup toggleGroup = new ToggleGroup(); - public FieldRowController(String fieldName, String leftValue, String rightValue, int rowIndex) { - fieldNameCell = new FieldNameCell(fieldName, rowIndex); + public ThreeFieldValues(Field field, String leftValue, String rightValue, int rowIndex, boolean groupsMerged) { + fieldNameCell = FieldNameCellFactory.create(field, rowIndex, groupsMerged); leftValueCell = new FieldValueCell(leftValue, rowIndex); rightValueCell = new FieldValueCell(rightValue, rowIndex); mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, rowIndex); this.leftValue = leftValue; this.rightValue = rightValue; + this.fieldName = field.getDisplayName(); toggleGroup.getToggles().addAll(leftValueCell, rightValueCell); toggleGroup.selectToggle(StringUtil.isNullOrEmpty(leftValue) ? rightValueCell : leftValueCell); diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java new file mode 100644 index 00000000000..4a87c2645cf --- /dev/null +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java @@ -0,0 +1,15 @@ +package org.jabref.gui.mergeentries.newmergedialog.cell; + +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; + +public class FieldNameCellFactory { + + public static FieldNameCell create(Field field, int rowIndex, boolean groupsMerged) { + if (field.equals(StandardField.GROUPS)) { + return new GroupsFieldNameCell("Groups", rowIndex, groupsMerged); + } else { + return new FieldNameCell(field.getDisplayName(), rowIndex); + } + } +} From dc02f37ad67a4101a0081fa5d2697e626342773e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:59:25 +0100 Subject: [PATCH 098/122] Perform experiments for implementing groups merging --- .../newmergedialog/ThreeWayMergeView.java | 71 ++++++++++++++++--- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 7ab42c83d2f..8aa1b9dc5ce 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -10,11 +10,13 @@ import javafx.scene.layout.VBox; import javafx.stage.Screen; +import org.jabref.gui.mergeentries.newmergedialog.cell.GroupsFieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.InternalField; +import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.EntryTypeFactory; public class ThreeWayMergeView extends VBox { @@ -33,7 +35,9 @@ public class ThreeWayMergeView extends VBox { private final GridPane mergeGridPane; private final ThreeWayMergeViewModel viewModel; - private final List fieldRowControllerList = new ArrayList<>(); + private final List threeFieldValuesList = new ArrayList<>(); + + private MergedGroups mergedGroupsRecord; public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { getStylesheets().add(ThreeWayMergeView.class.getResource("ThreeWayMergeView.css").toExternalForm()); @@ -71,9 +75,9 @@ private void initializeToolbar() { private void updateDiff() { if (toolbar.isShowDiffEnabled()) { - fieldRowControllerList.forEach(fieldRow -> fieldRow.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod()))); + threeFieldValuesList.forEach(fieldRow -> fieldRow.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod()))); } else { - fieldRowControllerList.forEach(FieldRowController::hideDiff); + threeFieldValuesList.forEach(ThreeFieldValues::hideDiff); } } @@ -101,13 +105,19 @@ private void initializeMergeGridPane() { mergeGridPane.getColumnConstraints().addAll(fieldNameColumnConstraints, leftEntryColumnConstraints, rightEntryColumnConstraints, mergedEntryColumnConstraints); for (int fieldIndex = 0; fieldIndex < viewModel.allFieldsSize(); fieldIndex++) { - addFieldRow(fieldIndex); + addFieldValues(viewModel.allFields().get(fieldIndex), fieldIndex); } } - private void addFieldRow(int index) { - Field field = viewModel.allFields().get(index); + private void addFieldName(Field field, int rowIndex) { + + } + + private Field getFieldAtIndex(int index) { + return viewModel.allFields().get(index); + } + private void addFieldValues(Field field, int index) { String leftEntryValue; String rightEntryValue; if (field.equals(InternalField.TYPE_HEADER)) { @@ -118,8 +128,38 @@ private void addFieldRow(int index) { rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); } - FieldRowController fieldRow = new FieldRowController(field.getDisplayName(), leftEntryValue, rightEntryValue, index); - fieldRowControllerList.add(fieldRow); + ThreeFieldValues fieldRow = new ThreeFieldValues(field, leftEntryValue, rightEntryValue, index, mergedGroupsRecord != null); + threeFieldValuesList.add(fieldRow); + + if (field.equals(StandardField.GROUPS)) { + // attach listener + GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldRow.getFieldNameCell(); + groupsField.setOnMergeGroups(() -> { + if (!fieldRow.hasEqualLeftAndRightValues()) { + removeRow(index); + String mergedGroups = mergeEntryGroups(); + viewModel.getLeftEntry().setField(field, mergedGroups); + viewModel.getRightEntry().setField(field, mergedGroups); + addFieldValues(field, index); + System.out.println("Groups merged: " + mergedGroups); + mergedGroupsRecord = new MergedGroups(leftEntryValue, rightEntryValue, mergedGroups); + } else { + System.out.println("Groups already have the same value"); + } + }); + + groupsField.setOnUnmergeGroups(() -> { + if (fieldRow.hasEqualLeftAndRightValues()) { + if (mergedGroupsRecord != null) { + viewModel.getLeftEntry().setField(field, mergedGroupsRecord.leftEntryGroups()); + viewModel.getRightEntry().setField(field, mergedGroupsRecord.rightEntryGroups()); + removeRow(index); + addFieldValues(field, index); + mergedGroupsRecord = null; + } + } + }); + } fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { @@ -143,6 +183,17 @@ private void addFieldRow(int index) { } } + public void removeRow(int index) { + threeFieldValuesList.remove(index); + mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index); + } + + private String mergeEntryGroups() { + // TODO: Update the merging logic + return viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse("") + + viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); + } + public BibEntry getMergedEntry() { return viewModel.getMergedEntry(); } @@ -156,11 +207,11 @@ public void setRightHeader(String rightHeader) { } public void selectLeftEntryValues() { - fieldRowControllerList.forEach(FieldRowController::selectLeftValue); + threeFieldValuesList.forEach(ThreeFieldValues::selectLeftValue); } public void selectRightEntryValues() { - fieldRowControllerList.forEach(FieldRowController::selectRightValue); + threeFieldValuesList.forEach(ThreeFieldValues::selectRightValue); } public void showDiff(ShowDiffConfig diffConfig) { From 6aac5fdd2fad6de0b806d11f17d4dcd15c8c5ae0 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 04:08:01 +0100 Subject: [PATCH 099/122] Delegate field name cells creation to ThreeWayMergeView - Seperated field values and field name cells because now it's easier to update field values cells without touching the field name cell --- .../newmergedialog/ThreeFieldValues.java | 13 +---------- .../newmergedialog/ThreeWayMergeView.java | 22 +++++++++++-------- .../cell/FieldNameCellFactory.java | 4 ++-- .../cell/GroupsFieldNameCell.java | 3 +-- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java index 45f34afe920..57e2a21f4a7 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java @@ -4,19 +4,16 @@ import javafx.scene.control.ToggleGroup; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; -import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCellFactory; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.SplitDiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.UnifiedDiffHighlighter; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; -import org.jabref.model.entry.field.Field; import org.jabref.model.strings.StringUtil; import org.fxmisc.richtext.StyleClassedTextArea; public class ThreeFieldValues { - private final FieldNameCell fieldNameCell; private final FieldValueCell leftValueCell; private FieldValueCell rightValueCell; private final MergedFieldCell mergedValueCell; @@ -25,19 +22,15 @@ public class ThreeFieldValues { private final String rightValue; - private final String fieldName; - private final ToggleGroup toggleGroup = new ToggleGroup(); - public ThreeFieldValues(Field field, String leftValue, String rightValue, int rowIndex, boolean groupsMerged) { - fieldNameCell = FieldNameCellFactory.create(field, rowIndex, groupsMerged); + public ThreeFieldValues(String leftValue, String rightValue, int rowIndex) { leftValueCell = new FieldValueCell(leftValue, rowIndex); rightValueCell = new FieldValueCell(rightValue, rowIndex); mergedValueCell = new MergedFieldCell(StringUtil.isNullOrEmpty(leftValue) ? rightValue : leftValue, rowIndex); this.leftValue = leftValue; this.rightValue = rightValue; - this.fieldName = field.getDisplayName(); toggleGroup.getToggles().addAll(leftValueCell, rightValueCell); toggleGroup.selectToggle(StringUtil.isNullOrEmpty(leftValue) ? rightValueCell : leftValueCell); @@ -86,10 +79,6 @@ public ReadOnlyStringProperty mergedValueProperty() { return mergedValueCell.textProperty(); } - public FieldNameCell getFieldNameCell() { - return fieldNameCell; - } - public FieldValueCell getLeftValueCell() { return leftValueCell; } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 8aa1b9dc5ce..647221b71b7 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -10,7 +10,8 @@ import javafx.scene.layout.VBox; import javafx.stage.Screen; -import org.jabref.gui.mergeentries.newmergedialog.cell.GroupsFieldNameCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; +import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCellFactory; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; @@ -105,12 +106,14 @@ private void initializeMergeGridPane() { mergeGridPane.getColumnConstraints().addAll(fieldNameColumnConstraints, leftEntryColumnConstraints, rightEntryColumnConstraints, mergedEntryColumnConstraints); for (int fieldIndex = 0; fieldIndex < viewModel.allFieldsSize(); fieldIndex++) { - addFieldValues(viewModel.allFields().get(fieldIndex), fieldIndex); + addFieldName(getFieldAtIndex(fieldIndex), fieldIndex); + addFieldValues(getFieldAtIndex(fieldIndex), fieldIndex); } } private void addFieldName(Field field, int rowIndex) { - + FieldNameCell fieldNameCell = FieldNameCellFactory.create(field, rowIndex); + mergeGridPane.add(fieldNameCell, 0, rowIndex); } private Field getFieldAtIndex(int index) { @@ -128,10 +131,10 @@ private void addFieldValues(Field field, int index) { rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); } - ThreeFieldValues fieldRow = new ThreeFieldValues(field, leftEntryValue, rightEntryValue, index, mergedGroupsRecord != null); + ThreeFieldValues fieldRow = new ThreeFieldValues(leftEntryValue, rightEntryValue, index); threeFieldValuesList.add(fieldRow); - if (field.equals(StandardField.GROUPS)) { +/* if (field.equals(StandardField.GROUPS)) { // attach listener GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldRow.getFieldNameCell(); groupsField.setOnMergeGroups(() -> { @@ -159,7 +162,7 @@ private void addFieldValues(Field field, int index) { } } }); - } + }*/ fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { @@ -175,11 +178,12 @@ private void addFieldValues(Field field, int index) { } if (fieldRow.hasEqualLeftAndRightValues()) { - mergeGridPane.add(fieldRow.getFieldNameCell(), 0, index, 1, 1); mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index, 2, 1); - mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index, 1, 1); + mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index); } else { - mergeGridPane.addRow(index, fieldRow.getFieldNameCell(), fieldRow.getLeftValueCell(), fieldRow.getRightValueCell(), fieldRow.getMergedValueCell()); + mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index); + mergeGridPane.add(fieldRow.getRightValueCell(), 1, index); + mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index); } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java index 4a87c2645cf..ecc352af4c0 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java @@ -5,9 +5,9 @@ public class FieldNameCellFactory { - public static FieldNameCell create(Field field, int rowIndex, boolean groupsMerged) { + public static FieldNameCell create(Field field, int rowIndex) { if (field.equals(StandardField.GROUPS)) { - return new GroupsFieldNameCell("Groups", rowIndex, groupsMerged); + return new GroupsFieldNameCell("Groups", rowIndex); } else { return new FieldNameCell(field.getDisplayName(), rowIndex); } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java index b20c1ffcb11..f70a7e79b1a 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java @@ -5,9 +5,8 @@ public class GroupsFieldNameCell extends FieldNameCell { private Runnable onUnmergeGroups; private boolean isMerged = false; - public GroupsFieldNameCell(String text, int rowIndex, boolean groupsMerged) { + public GroupsFieldNameCell(String text, int rowIndex) { super(text, rowIndex); - isMerged = groupsMerged; setOnMouseClicked(e -> { if (isMerged) { if (onUnmergeGroups != null) { From ce9735deded0698060190d06028ee52b320b4080 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 04:09:56 +0100 Subject: [PATCH 100/122] Improve groups merging function --- .../mergeentries/newmergedialog/ThreeWayMergeView.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 647221b71b7..999fc29a982 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -198,6 +198,14 @@ private String mergeEntryGroups() { viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); } + private String mergeLeftAndRightEntryGroups() { + List leftGroups = new ArrayList<>(Arrays.stream(viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse("").split(", ")).toList()); + List rightGroups = Arrays.stream(viewModel.getRightEntry().getField(StandardField.GROUPS).orElse("").split(", ")).toList(); + leftGroups.addAll(rightGroups); + + return String.join(", ", leftGroups); + } + public BibEntry getMergedEntry() { return viewModel.getMergedEntry(); } From 6e8e08f838643e59eb6877f391c5c3eb6ccc3bb5 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 04:12:08 +0100 Subject: [PATCH 101/122] Implement updateFieldValues to redraw the field row when left or right entry changes --- .../newmergedialog/ThreeWayMergeView.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 999fc29a982..8ed0268df2b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -1,6 +1,7 @@ package org.jabref.gui.mergeentries.newmergedialog; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javafx.scene.control.ScrollPane; @@ -12,6 +13,7 @@ import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCellFactory; +import org.jabref.gui.mergeentries.newmergedialog.cell.GroupsFieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; @@ -23,9 +25,11 @@ public class ThreeWayMergeView extends VBox { public static final int GRID_COLUMN_MIN_WIDTH = 250; + public static final String LEFT_DEFAULT_HEADER = Localization.lang("Left Entry"); public static final String RIGHT_DEFAULT_HEADER = Localization.lang("Right Entry"); + private static final int FIELD_NAME_COLUMN = 0; private final ColumnConstraints fieldNameColumnConstraints = new ColumnConstraints(150); private final ColumnConstraints leftEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); private final ColumnConstraints rightEntryColumnConstraints = new ColumnConstraints(GRID_COLUMN_MIN_WIDTH, 256, Double.MAX_VALUE); @@ -120,7 +124,8 @@ private Field getFieldAtIndex(int index) { return viewModel.allFields().get(index); } - private void addFieldValues(Field field, int index) { + private void addFieldValues(int fieldIndex) { + Field field = getFieldAtIndex(fieldIndex); String leftEntryValue; String rightEntryValue; if (field.equals(InternalField.TYPE_HEADER)) { @@ -178,24 +183,23 @@ private void addFieldValues(Field field, int index) { } if (fieldRow.hasEqualLeftAndRightValues()) { - mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index, 2, 1); - mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index); + mergeGridPane.add(fieldRow.getLeftValueCell(), 1, fieldIndex, 2, 1); + mergeGridPane.add(fieldRow.getMergedValueCell(), 3, fieldIndex); } else { - mergeGridPane.add(fieldRow.getLeftValueCell(), 1, index); - mergeGridPane.add(fieldRow.getRightValueCell(), 1, index); - mergeGridPane.add(fieldRow.getMergedValueCell(), 3, index); + mergeGridPane.add(fieldRow.getLeftValueCell(), 1, fieldIndex); + mergeGridPane.add(fieldRow.getRightValueCell(), 2, fieldIndex); + mergeGridPane.add(fieldRow.getMergedValueCell(), 3, fieldIndex); } } - public void removeRow(int index) { - threeFieldValuesList.remove(index); - mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index); + public void updateFieldValues(int fieldIndex) { + removeFieldValues(fieldIndex); + addFieldValues(fieldIndex); } - private String mergeEntryGroups() { - // TODO: Update the merging logic - return viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse("") + - viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); + public void removeFieldValues(int index) { + threeFieldValuesList.remove(index); + mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index && GridPane.getColumnIndex(node) > FIELD_NAME_COLUMN); } private String mergeLeftAndRightEntryGroups() { From d3aacaee309c928377a35a655983c8fd0dc8ca68 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 04:14:07 +0100 Subject: [PATCH 102/122] Write a draft version of the logic to merge groups --- .../newmergedialog/ThreeWayMergeView.java | 66 +++++++++---------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 8ed0268df2b..3096ddaef9c 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -111,13 +111,39 @@ private void initializeMergeGridPane() { for (int fieldIndex = 0; fieldIndex < viewModel.allFieldsSize(); fieldIndex++) { addFieldName(getFieldAtIndex(fieldIndex), fieldIndex); - addFieldValues(getFieldAtIndex(fieldIndex), fieldIndex); + addFieldValues(fieldIndex); } } - private void addFieldName(Field field, int rowIndex) { - FieldNameCell fieldNameCell = FieldNameCellFactory.create(field, rowIndex); - mergeGridPane.add(fieldNameCell, 0, rowIndex); + private void addFieldName(Field field, int fieldIndex) { + FieldNameCell fieldNameCell = FieldNameCellFactory.create(field, fieldIndex); + mergeGridPane.add(fieldNameCell, FIELD_NAME_COLUMN, fieldIndex); + + if (field.equals(StandardField.GROUPS)) { + GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldNameCell; + groupsField.setOnMergeGroups(() -> { + if (mergedGroupsRecord == null) { + String leftEntryValue = viewModel.getLeftEntry().getField(field).orElse(""); + String rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); + + String mergedGroups = mergeLeftAndRightEntryGroups(); + viewModel.getLeftEntry().setField(field, mergedGroups); + viewModel.getRightEntry().setField(field, mergedGroups); + System.out.println("Groups merged: " + mergedGroups); + mergedGroupsRecord = new MergedGroups(leftEntryValue, rightEntryValue, mergedGroups); + updateFieldValues(fieldIndex); + } + }); + + groupsField.setOnUnmergeGroups(() -> { + if (mergedGroupsRecord != null) { + viewModel.getLeftEntry().setField(field, mergedGroupsRecord.leftEntryGroups()); + viewModel.getRightEntry().setField(field, mergedGroupsRecord.rightEntryGroups()); + updateFieldValues(fieldIndex); + mergedGroupsRecord = null; + } + }); + } } private Field getFieldAtIndex(int index) { @@ -136,39 +162,9 @@ private void addFieldValues(int fieldIndex) { rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); } - ThreeFieldValues fieldRow = new ThreeFieldValues(leftEntryValue, rightEntryValue, index); + ThreeFieldValues fieldRow = new ThreeFieldValues(leftEntryValue, rightEntryValue, fieldIndex); threeFieldValuesList.add(fieldRow); -/* if (field.equals(StandardField.GROUPS)) { - // attach listener - GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldRow.getFieldNameCell(); - groupsField.setOnMergeGroups(() -> { - if (!fieldRow.hasEqualLeftAndRightValues()) { - removeRow(index); - String mergedGroups = mergeEntryGroups(); - viewModel.getLeftEntry().setField(field, mergedGroups); - viewModel.getRightEntry().setField(field, mergedGroups); - addFieldValues(field, index); - System.out.println("Groups merged: " + mergedGroups); - mergedGroupsRecord = new MergedGroups(leftEntryValue, rightEntryValue, mergedGroups); - } else { - System.out.println("Groups already have the same value"); - } - }); - - groupsField.setOnUnmergeGroups(() -> { - if (fieldRow.hasEqualLeftAndRightValues()) { - if (mergedGroupsRecord != null) { - viewModel.getLeftEntry().setField(field, mergedGroupsRecord.leftEntryGroups()); - viewModel.getRightEntry().setField(field, mergedGroupsRecord.rightEntryGroups()); - removeRow(index); - addFieldValues(field, index); - mergedGroupsRecord = null; - } - } - }); - }*/ - fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { getMergedEntry().setType(EntryTypeFactory.parse(mergedValue)); From 194be4d0b036014d9ef452a6a6965b9b7b419f6e Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 10:15:25 +0100 Subject: [PATCH 103/122] Refactoring --- .../newmergedialog/ThreeFieldValues.java | 4 +- .../newmergedialog/ThreeWayMergeView.java | 48 +++++++++++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java index 57e2a21f4a7..74bf76fe8b2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeFieldValues.java @@ -3,7 +3,6 @@ import javafx.beans.property.ReadOnlyStringProperty; import javafx.scene.control.ToggleGroup; -import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldValueCell; import org.jabref.gui.mergeentries.newmergedialog.cell.MergedFieldCell; import org.jabref.gui.mergeentries.newmergedialog.diffhighlighter.SplitDiffHighlighter; @@ -13,6 +12,9 @@ import org.fxmisc.richtext.StyleClassedTextArea; +/** + * A controller class to control left, right and merged field values + */ public class ThreeFieldValues { private final FieldValueCell leftValueCell; private FieldValueCell rightValueCell; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 3096ddaef9c..5a3f3b16ae7 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -121,31 +121,39 @@ private void addFieldName(Field field, int fieldIndex) { if (field.equals(StandardField.GROUPS)) { GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldNameCell; - groupsField.setOnMergeGroups(() -> { - if (mergedGroupsRecord == null) { - String leftEntryValue = viewModel.getLeftEntry().getField(field).orElse(""); - String rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); - - String mergedGroups = mergeLeftAndRightEntryGroups(); - viewModel.getLeftEntry().setField(field, mergedGroups); - viewModel.getRightEntry().setField(field, mergedGroups); - System.out.println("Groups merged: " + mergedGroups); - mergedGroupsRecord = new MergedGroups(leftEntryValue, rightEntryValue, mergedGroups); - updateFieldValues(fieldIndex); - } - }); - - groupsField.setOnUnmergeGroups(() -> { - if (mergedGroupsRecord != null) { - viewModel.getLeftEntry().setField(field, mergedGroupsRecord.leftEntryGroups()); - viewModel.getRightEntry().setField(field, mergedGroupsRecord.rightEntryGroups()); - updateFieldValues(fieldIndex); - mergedGroupsRecord = null; + groupsField.setOnAction(() -> { + if (areGroupsMerged()) { + unmergeGroups(); + } else { + mergeGroups(); } }); } } + public void mergeGroups() { + String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); + String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); + + String mergedGroups = mergeLeftAndRightEntryGroups(); + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); + + mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + } + + public void unmergeGroups() { + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroupsRecord.leftEntryGroups()); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroupsRecord.rightEntryGroups()); + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + mergedGroupsRecord = null; + } + + private boolean areGroupsMerged() { + return mergedGroupsRecord != null; + } + private Field getFieldAtIndex(int index) { return viewModel.allFields().get(index); } From 031e6f052cff4ff477aa9677de3592f3946f37f9 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 10:19:11 +0100 Subject: [PATCH 104/122] More Refactoring --- .../mergeentries/newmergedialog/ThreeWayMergeView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 5a3f3b16ae7..efe45679d0b 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -135,7 +135,7 @@ public void mergeGroups() { String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); - String mergedGroups = mergeLeftAndRightEntryGroups(); + String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); @@ -206,9 +206,9 @@ public void removeFieldValues(int index) { mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index && GridPane.getColumnIndex(node) > FIELD_NAME_COLUMN); } - private String mergeLeftAndRightEntryGroups() { - List leftGroups = new ArrayList<>(Arrays.stream(viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse("").split(", ")).toList()); - List rightGroups = Arrays.stream(viewModel.getRightEntry().getField(StandardField.GROUPS).orElse("").split(", ")).toList(); + private String mergeLeftAndRightEntryGroups(String left, String right) { + List leftGroups = new ArrayList<>(Arrays.stream(left.split(", ")).toList()); + List rightGroups = Arrays.stream(right.split(", ")).toList(); leftGroups.addAll(rightGroups); return String.join(", ", leftGroups); From 54f2ab3f6b298f9392d14d5e448425315fc0c43f Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 10:51:57 +0100 Subject: [PATCH 105/122] Merge groups only when left and right entries have different groups --- .../newmergedialog/ThreeWayMergeView.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index efe45679d0b..a6f93197779 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -135,12 +135,14 @@ public void mergeGroups() { String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); - String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); - viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); - viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); + if (!leftEntryGroups.equals(rightEntryGroups)) { + String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); - mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); - updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + } } public void unmergeGroups() { From d1a3ed9e981b615db4686e671233ef1d7063e5d2 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 10:53:44 +0100 Subject: [PATCH 106/122] Add row constraints to fix UI lagging when updating field values --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index a6f93197779..77d78663be6 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -8,6 +8,7 @@ import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; import javafx.scene.layout.VBox; import javafx.stage.Screen; @@ -112,6 +113,12 @@ private void initializeMergeGridPane() { for (int fieldIndex = 0; fieldIndex < viewModel.allFieldsSize(); fieldIndex++) { addFieldName(getFieldAtIndex(fieldIndex), fieldIndex); addFieldValues(fieldIndex); + + if (getFieldAtIndex(fieldIndex).equals(StandardField.GROUPS)) { + mergeGridPane.getRowConstraints().add(fieldIndex, new RowConstraints(56, 56, 56)); + } else { + mergeGridPane.getRowConstraints().add(new RowConstraints()); + } } } From c217cf82d1313fbd46e70c27ad7b1822760ade63 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 11:01:07 +0100 Subject: [PATCH 107/122] Minor refactoring --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 77d78663be6..c7604ecca2c 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -114,6 +114,7 @@ private void initializeMergeGridPane() { addFieldName(getFieldAtIndex(fieldIndex), fieldIndex); addFieldValues(fieldIndex); + // Removing this will cause a UI lag when updating field values if (getFieldAtIndex(fieldIndex).equals(StandardField.GROUPS)) { mergeGridPane.getRowConstraints().add(fieldIndex, new RowConstraints(56, 56, 56)); } else { @@ -205,12 +206,12 @@ private void addFieldValues(int fieldIndex) { } } - public void updateFieldValues(int fieldIndex) { + private void updateFieldValues(int fieldIndex) { removeFieldValues(fieldIndex); addFieldValues(fieldIndex); } - public void removeFieldValues(int index) { + private void removeFieldValues(int index) { threeFieldValuesList.remove(index); mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index && GridPane.getColumnIndex(node) > FIELD_NAME_COLUMN); } From 0ea0da19ab354c8bab34c5746eb505cadfef2183 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 11:06:55 +0100 Subject: [PATCH 108/122] Don't merge common groups between left and right entries --- .../newmergedialog/ThreeWayMergeView.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index c7604ecca2c..684a09dcad4 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -2,7 +2,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javafx.scene.control.ScrollPane; import javafx.scene.layout.ColumnConstraints; @@ -41,7 +43,7 @@ public class ThreeWayMergeView extends VBox { private final GridPane mergeGridPane; private final ThreeWayMergeViewModel viewModel; - private final List threeFieldValuesList = new ArrayList<>(); + private final List fieldValuesList = new ArrayList<>(); private MergedGroups mergedGroupsRecord; @@ -81,9 +83,9 @@ private void initializeToolbar() { private void updateDiff() { if (toolbar.isShowDiffEnabled()) { - threeFieldValuesList.forEach(fieldRow -> fieldRow.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod()))); + fieldValuesList.forEach(fieldValues -> fieldValues.showDiff(new ShowDiffConfig(toolbar.getDiffView(), toolbar.getDiffHighlightingMethod()))); } else { - threeFieldValuesList.forEach(ThreeFieldValues::hideDiff); + fieldValuesList.forEach(ThreeFieldValues::hideDiff); } } @@ -114,7 +116,7 @@ private void initializeMergeGridPane() { addFieldName(getFieldAtIndex(fieldIndex), fieldIndex); addFieldValues(fieldIndex); - // Removing this will cause a UI lag when updating field values + // Removing this will cause UI to lag when updating field values if (getFieldAtIndex(fieldIndex).equals(StandardField.GROUPS)) { mergeGridPane.getRowConstraints().add(fieldIndex, new RowConstraints(56, 56, 56)); } else { @@ -181,7 +183,7 @@ private void addFieldValues(int fieldIndex) { } ThreeFieldValues fieldRow = new ThreeFieldValues(leftEntryValue, rightEntryValue, fieldIndex); - threeFieldValuesList.add(fieldRow); + fieldValuesList.add(fieldRow); fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { @@ -212,12 +214,12 @@ private void updateFieldValues(int fieldIndex) { } private void removeFieldValues(int index) { - threeFieldValuesList.remove(index); + fieldValuesList.remove(index); mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index && GridPane.getColumnIndex(node) > FIELD_NAME_COLUMN); } private String mergeLeftAndRightEntryGroups(String left, String right) { - List leftGroups = new ArrayList<>(Arrays.stream(left.split(", ")).toList()); + Set leftGroups = new HashSet<>(Arrays.stream(left.split(", ")).toList()); List rightGroups = Arrays.stream(right.split(", ")).toList(); leftGroups.addAll(rightGroups); @@ -237,11 +239,11 @@ public void setRightHeader(String rightHeader) { } public void selectLeftEntryValues() { - threeFieldValuesList.forEach(ThreeFieldValues::selectLeftValue); + fieldValuesList.forEach(ThreeFieldValues::selectLeftValue); } public void selectRightEntryValues() { - threeFieldValuesList.forEach(ThreeFieldValues::selectRightValue); + fieldValuesList.forEach(ThreeFieldValues::selectRightValue); } public void showDiff(ShowDiffConfig diffConfig) { From 675669fcdb956652ac600c6d91d55e9f666a34ea Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 22:17:35 +0100 Subject: [PATCH 109/122] Refactor GroupsFieldNameCell constructor --- .../mergeentries/newmergedialog/cell/FieldNameCellFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java index ecc352af4c0..01a2fa90aff 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCellFactory.java @@ -7,7 +7,7 @@ public class FieldNameCellFactory { public static FieldNameCell create(Field field, int rowIndex) { if (field.equals(StandardField.GROUPS)) { - return new GroupsFieldNameCell("Groups", rowIndex); + return new GroupsFieldNameCell(rowIndex); } else { return new FieldNameCell(field.getDisplayName(), rowIndex); } From e9ab51df2c3e756e96a09acdec7c4c1438bc445a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 22:20:05 +0100 Subject: [PATCH 110/122] Allow groups field row to grow in height --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 684a09dcad4..37ba0212e69 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -118,7 +118,7 @@ private void initializeMergeGridPane() { // Removing this will cause UI to lag when updating field values if (getFieldAtIndex(fieldIndex).equals(StandardField.GROUPS)) { - mergeGridPane.getRowConstraints().add(fieldIndex, new RowConstraints(56, 56, 56)); + mergeGridPane.getRowConstraints().add(fieldIndex, new RowConstraints(56, 56, Double.MAX_VALUE)); } else { mergeGridPane.getRowConstraints().add(new RowConstraints()); } From d166273ea3519f36e3feddc5635608d6f57440ac Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 22:25:09 +0100 Subject: [PATCH 111/122] Fix select all left/right not working properly --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 37ba0212e69..61401763dcd 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -183,7 +183,7 @@ private void addFieldValues(int fieldIndex) { } ThreeFieldValues fieldRow = new ThreeFieldValues(leftEntryValue, rightEntryValue, fieldIndex); - fieldValuesList.add(fieldRow); + fieldValuesList.add(fieldIndex, fieldRow); fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { From 2f4d6d3837fa6bca92000e7258c3471df750c2e6 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 22:29:39 +0100 Subject: [PATCH 112/122] Update diff when a field value changes --- .../newmergedialog/ThreeWayMergeView.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 61401763dcd..331a2a4e970 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -182,10 +182,10 @@ private void addFieldValues(int fieldIndex) { rightEntryValue = viewModel.getRightEntry().getField(field).orElse(""); } - ThreeFieldValues fieldRow = new ThreeFieldValues(leftEntryValue, rightEntryValue, fieldIndex); - fieldValuesList.add(fieldIndex, fieldRow); + ThreeFieldValues fieldValues = new ThreeFieldValues(leftEntryValue, rightEntryValue, fieldIndex); + fieldValuesList.add(fieldIndex, fieldValues); - fieldRow.mergedValueProperty().addListener((observable, old, mergedValue) -> { + fieldValues.mergedValueProperty().addListener((observable, old, mergedValue) -> { if (field.equals(InternalField.TYPE_HEADER)) { getMergedEntry().setType(EntryTypeFactory.parse(mergedValue)); } else { @@ -193,24 +193,25 @@ private void addFieldValues(int fieldIndex) { } }); if (field.equals(InternalField.TYPE_HEADER)) { - getMergedEntry().setType(EntryTypeFactory.parse(fieldRow.getMergedValue())); + getMergedEntry().setType(EntryTypeFactory.parse(fieldValues.getMergedValue())); } else { - getMergedEntry().setField(field, fieldRow.getMergedValue()); + getMergedEntry().setField(field, fieldValues.getMergedValue()); } - if (fieldRow.hasEqualLeftAndRightValues()) { - mergeGridPane.add(fieldRow.getLeftValueCell(), 1, fieldIndex, 2, 1); - mergeGridPane.add(fieldRow.getMergedValueCell(), 3, fieldIndex); + if (fieldValues.hasEqualLeftAndRightValues()) { + mergeGridPane.add(fieldValues.getLeftValueCell(), 1, fieldIndex, 2, 1); + mergeGridPane.add(fieldValues.getMergedValueCell(), 3, fieldIndex); } else { - mergeGridPane.add(fieldRow.getLeftValueCell(), 1, fieldIndex); - mergeGridPane.add(fieldRow.getRightValueCell(), 2, fieldIndex); - mergeGridPane.add(fieldRow.getMergedValueCell(), 3, fieldIndex); + mergeGridPane.add(fieldValues.getLeftValueCell(), 1, fieldIndex); + mergeGridPane.add(fieldValues.getRightValueCell(), 2, fieldIndex); + mergeGridPane.add(fieldValues.getMergedValueCell(), 3, fieldIndex); } } private void updateFieldValues(int fieldIndex) { removeFieldValues(fieldIndex); addFieldValues(fieldIndex); + updateDiff(); } private void removeFieldValues(int index) { From 9463db7bad2c838e7b09c586e34969e60456a0b3 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 23:16:18 +0100 Subject: [PATCH 113/122] Add the merge icon and style css --- .../java/org/jabref/gui/icon/IconTheme.java | 4 ++- .../newmergedialog/ThreeWayMergeView.css | 6 ++++ .../newmergedialog/cell/FieldNameCell.java | 29 +++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index 8ef3ed5d490..80eb1824a9b 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -343,7 +343,9 @@ public enum JabRefIcons implements JabRefIcon { ACCEPT_LEFT(MaterialDesignS.SUBDIRECTORY_ARROW_LEFT), - ACCEPT_RIGHT(MaterialDesignS.SUBDIRECTORY_ARROW_RIGHT); + ACCEPT_RIGHT(MaterialDesignS.SUBDIRECTORY_ARROW_RIGHT), + + MERGE_GROUPS(MaterialDesignS.SOURCE_MERGE); private final JabRefIcon icon; diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css index 416b55b308a..ac73d7593f0 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.css @@ -43,3 +43,9 @@ -fx-font-weight: bold; -fx-padding: 1, 0, 1, 0; } + +.field-name .glyph-icon, +.field-name .ikonli-font-icon { + -fx-icon-size: 17; + -fx-icon-color: -jr-theme-text; +} diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index 93b0b20d84e..95664eb7566 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -1,16 +1,30 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; -import org.jabref.gui.actions.Action; +import org.jabref.gui.Globals; +import org.jabref.gui.actions.ActionFactory; +import org.jabref.gui.icon.IconTheme; +import org.jabref.logic.l10n.Localization; + +import de.saxsys.mvvmfx.utils.commands.Command; /** * A non-editable cell that contains the name of some field */ public class FieldNameCell extends AbstractCell { public static final String DEFAULT_STYLE_CLASS = "field-name"; + protected final HBox actionLayout = new HBox(); private final Label label = new Label(); + private final HBox labelBox = new HBox(label); + private final ActionFactory factory = new ActionFactory(Globals.getKeyPrefs()); + public FieldNameCell(String text, int rowIndex) { super(text, rowIndex); initialize(); @@ -19,10 +33,21 @@ public FieldNameCell(String text, int rowIndex) { private void initialize() { getStyleClass().add(DEFAULT_STYLE_CLASS); initializeLabel(); - getChildren().add(label); + getChildren().addAll(labelBox, actionLayout); } private void initializeLabel() { label.textProperty().bind(textProperty()); + HBox.setHgrow(labelBox, Priority.ALWAYS); + } + + protected void addAction(String actionName, IconTheme.JabRefIcons icon, Command command) { + Node iconNode = icon.getGraphicNode(); + Button actionButton = factory.createIconButton(() -> Localization.lang(actionName), command); + actionButton.setGraphic(iconNode); + actionButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + actionButton.setMaxHeight(Double.MAX_VALUE); + + actionLayout.getChildren().add(actionButton); } } From 0992b22caa1c9e961b18eb96919c88050a9b33ac Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Fri, 8 Jul 2022 23:31:31 +0100 Subject: [PATCH 114/122] Extract merge and unmerge commands into their own classes --- .../newmergedialog/ThreeWayMergeView.java | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 331a2a4e970..ab1cd97b6d1 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -14,6 +14,7 @@ import javafx.scene.layout.VBox; import javafx.stage.Screen; +import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCellFactory; import org.jabref.gui.mergeentries.newmergedialog.cell.GroupsFieldNameCell; @@ -131,37 +132,11 @@ private void addFieldName(Field field, int fieldIndex) { if (field.equals(StandardField.GROUPS)) { GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldNameCell; - groupsField.setOnAction(() -> { - if (areGroupsMerged()) { - unmergeGroups(); - } else { - mergeGroups(); - } - }); - } - } - - public void mergeGroups() { - String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); - String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); - - if (!leftEntryGroups.equals(rightEntryGroups)) { - String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); - viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); - viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); - - mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); - updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + groupsField.setMergeGroupsCommand(new MergeGroupsCommand()); + groupsField.setUnmergeGroupsCommand(new UnmergeGroupsCommand()); } } - public void unmergeGroups() { - viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroupsRecord.leftEntryGroups()); - viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroupsRecord.rightEntryGroups()); - updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); - mergedGroupsRecord = null; - } - private boolean areGroupsMerged() { return mergedGroupsRecord != null; } @@ -252,4 +227,36 @@ public void showDiff(ShowDiffConfig diffConfig) { toolbar.setDiffHighlightingMethod(diffConfig.diffHighlightingMethod()); toolbar.setShowDiff(true); } + + public class MergeGroupsCommand extends SimpleCommand { + @Override + public void execute() { + if (!areGroupsMerged()) { + String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); + String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); + + if (!leftEntryGroups.equals(rightEntryGroups)) { + String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); + + mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + } + } + } + } + + public class UnmergeGroupsCommand extends SimpleCommand { + + @Override + public void execute() { + if (areGroupsMerged()) { + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroupsRecord.leftEntryGroups()); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroupsRecord.rightEntryGroups()); + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + mergedGroupsRecord = null; + } + } + } } From 05518ea32f77073c107e7460d16ba19020f39973 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 02:36:10 +0100 Subject: [PATCH 115/122] Refactoring --- .../newmergedialog/ThreeWayMergeView.java | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index ab1cd97b6d1..c69695f36fa 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -6,6 +6,9 @@ import java.util.List; import java.util.Set; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.scene.control.ScrollPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; @@ -48,6 +51,9 @@ public class ThreeWayMergeView extends VBox { private MergedGroups mergedGroupsRecord; + private final BooleanProperty areGroupsMerged = new SimpleBooleanProperty(); + + public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { getStylesheets().add(ThreeWayMergeView.class.getResource("ThreeWayMergeView.css").toExternalForm()); viewModel = new ThreeWayMergeViewModel(leftEntry, rightEntry, leftHeader, rightHeader); @@ -132,8 +138,8 @@ private void addFieldName(Field field, int fieldIndex) { if (field.equals(StandardField.GROUPS)) { GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldNameCell; - groupsField.setMergeGroupsCommand(new MergeGroupsCommand()); - groupsField.setUnmergeGroupsCommand(new UnmergeGroupsCommand()); + groupsField.setMergeGroupsCommand(new MergeGroupsCommand(groupsField)); + groupsField.setUnmergeGroupsCommand(new UnmergeGroupsCommand(groupsField)); } } @@ -229,34 +235,61 @@ public void showDiff(ShowDiffConfig diffConfig) { } public class MergeGroupsCommand extends SimpleCommand { + private final GroupsFieldNameCell groupsFieldNameCell; + + public MergeGroupsCommand(GroupsFieldNameCell groupsFieldCell) { + this.groupsFieldNameCell = groupsFieldCell; + + this.executable.bind(Bindings.and(areGroupsMerged.not(), Bindings.createBooleanBinding(() -> { + String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); + String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); + + return !leftEntryGroups.equals(rightEntryGroups); + }))); + } + @Override public void execute() { - if (!areGroupsMerged()) { + assert !areGroupsMerged(); + String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); - if (!leftEntryGroups.equals(rightEntryGroups)) { - String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); - viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); - viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); + assert !leftEntryGroups.equals(rightEntryGroups); - mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); - updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); - } - } + String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); + + mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + groupsFieldNameCell.toggleMergeProperty().set(GroupsFieldNameCell.ToggleMerge.UNMERGE); } } public class UnmergeGroupsCommand extends SimpleCommand { + private final GroupsFieldNameCell groupsFieldCell; + + public UnmergeGroupsCommand(GroupsFieldNameCell groupsFieldCell) { + this.groupsFieldCell = groupsFieldCell; + } @Override public void execute() { - if (areGroupsMerged()) { + assert areGroupsMerged(); viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroupsRecord.leftEntryGroups()); viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroupsRecord.rightEntryGroups()); updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); mergedGroupsRecord = null; - } + groupsFieldCell.toggleMergeProperty().set(GroupsFieldNameCell.ToggleMerge.MERGE); } } + + /* + * Notes + * 1. Left and Right could have the same value + * + * Merge Button Clicked: + * 1. + * */ } From b38eeec7f85ec07b3a25a616cf33031b1d5c942d Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 02:59:39 +0100 Subject: [PATCH 116/122] Record groups merging operation using CompoundEdit - Exposed a public API to be able to cancel groups merge --- .../newmergedialog/ThreeWayMergeView.java | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index c69695f36fa..5d29549d8ef 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.Set; +import javax.swing.undo.CompoundEdit; + import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -22,6 +24,7 @@ import org.jabref.gui.mergeentries.newmergedialog.cell.FieldNameCellFactory; import org.jabref.gui.mergeentries.newmergedialog.cell.GroupsFieldNameCell; import org.jabref.gui.mergeentries.newmergedialog.toolbar.ThreeWayMergeToolbar; +import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; @@ -49,10 +52,9 @@ public class ThreeWayMergeView extends VBox { private final ThreeWayMergeViewModel viewModel; private final List fieldValuesList = new ArrayList<>(); - private MergedGroups mergedGroupsRecord; - private final BooleanProperty areGroupsMerged = new SimpleBooleanProperty(); + private final CompoundEdit mergeGroupsEdit = new CompoundEdit(); public ThreeWayMergeView(BibEntry leftEntry, BibEntry rightEntry, String leftHeader, String rightHeader) { getStylesheets().add(ThreeWayMergeView.class.getResource("ThreeWayMergeView.css").toExternalForm()); @@ -143,10 +145,6 @@ private void addFieldName(Field field, int fieldIndex) { } } - private boolean areGroupsMerged() { - return mergedGroupsRecord != null; - } - private Field getFieldAtIndex(int index) { return viewModel.allFields().get(index); } @@ -234,6 +232,12 @@ public void showDiff(ShowDiffConfig diffConfig) { toolbar.setShowDiff(true); } + public void cancelGroupsMerge() { + if (mergeGroupsEdit.canUndo()) { + mergeGroupsEdit.undo(); + } + } + public class MergeGroupsCommand extends SimpleCommand { private final GroupsFieldNameCell groupsFieldNameCell; @@ -250,20 +254,28 @@ public MergeGroupsCommand(GroupsFieldNameCell groupsFieldCell) { @Override public void execute() { - assert !areGroupsMerged(); + BibEntry leftEntry = viewModel.getLeftEntry(); + BibEntry rightEntry = viewModel.getRightEntry(); - String leftEntryGroups = viewModel.getLeftEntry().getField(StandardField.GROUPS).orElse(""); - String rightEntryGroups = viewModel.getRightEntry().getField(StandardField.GROUPS).orElse(""); + String leftEntryGroups = leftEntry.getField(StandardField.GROUPS).orElse(""); + String rightEntryGroups = rightEntry.getField(StandardField.GROUPS).orElse(""); - assert !leftEntryGroups.equals(rightEntryGroups); + assert !leftEntryGroups.equals(rightEntryGroups); - String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); - viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); - viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); + String mergedGroups = mergeLeftAndRightEntryGroups(leftEntryGroups, rightEntryGroups); + viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroups); + viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroups); - mergedGroupsRecord = new MergedGroups(leftEntryGroups, rightEntryGroups, mergedGroups); - updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); - groupsFieldNameCell.toggleMergeProperty().set(GroupsFieldNameCell.ToggleMerge.UNMERGE); + if (mergeGroupsEdit.canRedo()) { + mergeGroupsEdit.redo(); + } else { + mergeGroupsEdit.addEdit(new UndoableFieldChange(leftEntry, StandardField.GROUPS, leftEntryGroups, mergedGroups)); + mergeGroupsEdit.addEdit(new UndoableFieldChange(rightEntry, StandardField.GROUPS, rightEntryGroups, mergedGroups)); + mergeGroupsEdit.end(); + } + + updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); + groupsFieldNameCell.mergeActionProperty().set(GroupsFieldNameCell.MergeAction.UNMERGE); } } @@ -272,24 +284,16 @@ public class UnmergeGroupsCommand extends SimpleCommand { public UnmergeGroupsCommand(GroupsFieldNameCell groupsFieldCell) { this.groupsFieldCell = groupsFieldCell; + this.executable.bind(Bindings.createBooleanBinding(mergeGroupsEdit::canUndo)); } @Override public void execute() { - assert areGroupsMerged(); - viewModel.getLeftEntry().setField(StandardField.GROUPS, mergedGroupsRecord.leftEntryGroups()); - viewModel.getRightEntry().setField(StandardField.GROUPS, mergedGroupsRecord.rightEntryGroups()); + if (mergeGroupsEdit.canUndo()) { + mergeGroupsEdit.undo(); updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); - mergedGroupsRecord = null; - groupsFieldCell.toggleMergeProperty().set(GroupsFieldNameCell.ToggleMerge.MERGE); + groupsFieldCell.mergeActionProperty().set(GroupsFieldNameCell.MergeAction.MERGE); + } } } - - /* - * Notes - * 1. Left and Right could have the same value - * - * Merge Button Clicked: - * 1. - * */ } From 0d4a8c2f0b46d00a4966d7ce7f9d9e497830fd15 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:25:57 +0100 Subject: [PATCH 117/122] Cancel groups merge when user choose 'Cancel' in the merge entries dialog --- .../java/org/jabref/gui/mergeentries/MergeEntriesDialog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java index 3486a9702d2..4c2f5ace0ec 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesDialog.java @@ -33,6 +33,7 @@ private void init() { if (buttonType.equals(replaceEntries)) { return threeWayMergeView.getMergedEntry(); } else { + threeWayMergeView.cancelGroupsMerge(); return null; } }); From 3469ffc4dfff7832b1930e92b70715e7d5ddd098 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:28:38 +0100 Subject: [PATCH 118/122] Make FieldNameCell take only one action --- .../gui/mergeentries/newmergedialog/cell/FieldNameCell.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index 95664eb7566..5391b0f0b4c 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -41,7 +41,8 @@ private void initializeLabel() { HBox.setHgrow(labelBox, Priority.ALWAYS); } - protected void addAction(String actionName, IconTheme.JabRefIcons icon, Command command) { + protected void setAction(String actionName, IconTheme.JabRefIcons icon, Command command) { + actionLayout.getChildren().clear(); Node iconNode = icon.getGraphicNode(); Button actionButton = factory.createIconButton(() -> Localization.lang(actionName), command); actionButton.setGraphic(iconNode); From 22eea6418da6a558cc11b9f723c810bfe26a1784 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:46:43 +0100 Subject: [PATCH 119/122] Set groups merge action to MERGE by default --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 5d29549d8ef..eba9651eba7 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -142,6 +142,7 @@ private void addFieldName(Field field, int fieldIndex) { GroupsFieldNameCell groupsField = (GroupsFieldNameCell) fieldNameCell; groupsField.setMergeGroupsCommand(new MergeGroupsCommand(groupsField)); groupsField.setUnmergeGroupsCommand(new UnmergeGroupsCommand(groupsField)); + groupsField.setMergeAction(GroupsFieldNameCell.MergeAction.MERGE); } } From 8809bea96f92e3fd8287fa9ec1fa84a534c0fc42 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:50:08 +0100 Subject: [PATCH 120/122] Fix Unmerge button always disabled --- .../gui/mergeentries/newmergedialog/ThreeWayMergeView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index eba9651eba7..7e94f06bd4e 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -285,7 +285,6 @@ public class UnmergeGroupsCommand extends SimpleCommand { public UnmergeGroupsCommand(GroupsFieldNameCell groupsFieldCell) { this.groupsFieldCell = groupsFieldCell; - this.executable.bind(Bindings.createBooleanBinding(mergeGroupsEdit::canUndo)); } @Override From 30b90bd78195d60d88f93cf4c353216ddd329d5a Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 03:52:35 +0100 Subject: [PATCH 121/122] Cleanup --- .../newmergedialog/ThreeWayMergeView.java | 20 +++++++++---------- .../newmergedialog/cell/FieldNameCell.java | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java index 7e94f06bd4e..2194dd19c08 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/ThreeWayMergeView.java @@ -199,14 +199,6 @@ private void removeFieldValues(int index) { mergeGridPane.getChildren().removeIf(node -> GridPane.getRowIndex(node) == index && GridPane.getColumnIndex(node) > FIELD_NAME_COLUMN); } - private String mergeLeftAndRightEntryGroups(String left, String right) { - Set leftGroups = new HashSet<>(Arrays.stream(left.split(", ")).toList()); - List rightGroups = Arrays.stream(right.split(", ")).toList(); - leftGroups.addAll(rightGroups); - - return String.join(", ", leftGroups); - } - public BibEntry getMergedEntry() { return viewModel.getMergedEntry(); } @@ -276,7 +268,15 @@ public void execute() { } updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); - groupsFieldNameCell.mergeActionProperty().set(GroupsFieldNameCell.MergeAction.UNMERGE); + groupsFieldNameCell.setMergeAction(GroupsFieldNameCell.MergeAction.UNMERGE); + } + + private String mergeLeftAndRightEntryGroups(String left, String right) { + Set leftGroups = new HashSet<>(Arrays.stream(left.split(", ")).toList()); + List rightGroups = Arrays.stream(right.split(", ")).toList(); + leftGroups.addAll(rightGroups); + + return String.join(", ", leftGroups); } } @@ -292,7 +292,7 @@ public void execute() { if (mergeGroupsEdit.canUndo()) { mergeGroupsEdit.undo(); updateFieldValues(viewModel.allFields().indexOf(StandardField.GROUPS)); - groupsFieldCell.mergeActionProperty().set(GroupsFieldNameCell.MergeAction.MERGE); + groupsFieldCell.setMergeAction(GroupsFieldNameCell.MergeAction.MERGE); } } } diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java index 5391b0f0b4c..c00d55ba4aa 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/FieldNameCell.java @@ -44,7 +44,7 @@ private void initializeLabel() { protected void setAction(String actionName, IconTheme.JabRefIcons icon, Command command) { actionLayout.getChildren().clear(); Node iconNode = icon.getGraphicNode(); - Button actionButton = factory.createIconButton(() -> Localization.lang(actionName), command); + Button actionButton = factory.createIconButton(() -> actionName, command); actionButton.setGraphic(iconNode); actionButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); actionButton.setMaxHeight(Double.MAX_VALUE); From 421705779179ff546a09067a2f8de6743ea54fb5 Mon Sep 17 00:00:00 2001 From: Houssem Nasri Date: Sat, 9 Jul 2022 04:31:28 +0100 Subject: [PATCH 122/122] Almost f**** everything up. I deleted GroupsFieldNameCell content by mistake, it's all ok now - I did a rebase and then when resolving conflicts, I chose "Accept Theirs" all the way but that didn't go well, now I have no history of GroupsFieldNameCell --- .../cell/GroupsFieldNameCell.java | 56 ++++++++++++------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java index f70a7e79b1a..94a232cf3d2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java +++ b/src/main/java/org/jabref/gui/mergeentries/newmergedialog/cell/GroupsFieldNameCell.java @@ -1,32 +1,48 @@ package org.jabref.gui.mergeentries.newmergedialog.cell; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +import org.jabref.gui.icon.IconTheme; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.field.StandardField; + +import de.saxsys.mvvmfx.utils.commands.Command; + public class GroupsFieldNameCell extends FieldNameCell { - private Runnable onMergeGroups; - private Runnable onUnmergeGroups; - private boolean isMerged = false; - - public GroupsFieldNameCell(String text, int rowIndex) { - super(text, rowIndex); - setOnMouseClicked(e -> { - if (isMerged) { - if (onUnmergeGroups != null) { - onUnmergeGroups.run(); - isMerged = false; - } + private Command mergeGroupsCommand; + private Command unmergeGroupsCommand; + + private final ObjectProperty mergeAction = new SimpleObjectProperty<>(); + + public GroupsFieldNameCell(int rowIndex) { + super(StandardField.GROUPS.getDisplayName(), rowIndex); + mergeActionProperty().addListener((obs, old, newValue) -> { + if (newValue == MergeAction.MERGE) { + setAction(Localization.lang("Merge Groups"), IconTheme.JabRefIcons.MERGE_GROUPS, mergeGroupsCommand); } else { - if (onMergeGroups != null) { - onMergeGroups.run(); - isMerged = true; - } + setAction(Localization.lang("Unmerge Groups"), IconTheme.JabRefIcons.UNDO, unmergeGroupsCommand); } }); } - public void setOnMergeGroups(Runnable mergeGroups) { - this.onMergeGroups = mergeGroups; + public void setMergeGroupsCommand(Command mergeGroupsCommand) { + this.mergeGroupsCommand = mergeGroupsCommand; + } + + public void setUnmergeGroupsCommand(Command unmergeGroupsCommand) { + this.unmergeGroupsCommand = unmergeGroupsCommand; + } + + public ObjectProperty mergeActionProperty() { + return mergeAction; + } + + public void setMergeAction(MergeAction mergeAction) { + mergeActionProperty().set(mergeAction); } - public void setOnUnmergeGroups(Runnable unmergeGroups) { - this.onUnmergeGroups = unmergeGroups; + public enum MergeAction { + MERGE, UNMERGE } }