From 9a12d35622ad2c44495b30cca9321f17d1c4c388 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Fri, 28 Feb 2020 21:22:36 -0800 Subject: [PATCH 01/17] issue 5847 --- .../gui/entryeditor/FieldsEditorTab.java | 2 +- .../gui/fieldeditors/FieldNameLabel.java | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 603450b39ce..4a2db4f4087 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -97,7 +97,7 @@ private void setupPanel(BibEntry entry, boolean compressed) { fieldEditor.bindToEntry(entry); editors.put(field, fieldEditor); - labels.add(new FieldNameLabel(field)); + labels.add(new FieldNameLabel(field, field.getName())); } ColumnConstraints columnExpand = new ColumnConstraints(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index f36eafbb808..13aa95cf308 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -4,15 +4,67 @@ import javafx.geometry.Pos; import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; public class FieldNameLabel extends Label { + private Tooltip tip; + private static final String AUTHOR = "author"; + private static final String BIBTEXKEY = "bibtexkey"; + private static final String JOURNAL = "journal"; + private static final String TITLE = "title"; + private static final String YEAR = "year"; - public FieldNameLabel(Field field) { + private static final String AUTHOR_TIP = "Multiple authors separated with 'and', e.g. author1 and author2"; + private static final String BIBTEXKEY_TIP = "[First author'last name][Article year] e.g. Jones2020"; + private static final String JOURNAL_TIP = "The name of the journal"; + private static final String TITLE_TIP = "The title of the article"; + private static final String YEAR_TIP = "The year of publication, e.g. 2005"; + + public FieldNameLabel(Field field, String name) { super(field.getDisplayName()); setPadding(new Insets(4, 0, 0, 0)); setAlignment(Pos.CENTER); setPrefHeight(Double.POSITIVE_INFINITY); + setTip(name); + } + + public void setTip(String name){ + tip = new Tooltip(); + + switch (name){ + case AUTHOR: + { + tip.setText(AUTHOR_TIP); + break; + } + case BIBTEXKEY: + { + tip.setText(BIBTEXKEY_TIP); + break; + } + case JOURNAL: + { + tip.setText(JOURNAL_TIP); + break; + } + case TITLE: + { + tip.setText(TITLE_TIP); + break; + } + case YEAR: + { + tip.setText(YEAR_TIP); + break; + } + + default:{ + return; + } + } + this.setTooltip(tip); } } From a31f8ae8b5493d6e1b139fc6ccdf1c4d12617cd5 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Wed, 4 Mar 2020 17:50:19 -0800 Subject: [PATCH 02/17] fix PR#6046 problems --- .../gui/fieldeditors/FieldNameLabel.java | 57 +++++-------------- .../model/entry/field/StandardField.java | 5 +- src/main/resources/l10n/JabRef_en.properties | 6 ++ 3 files changed, 22 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index 13aa95cf308..1cae5d8ac09 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -3,24 +3,14 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; - import javafx.scene.control.Tooltip; + +import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.StandardField; public class FieldNameLabel extends Label { private Tooltip tip; - private static final String AUTHOR = "author"; - private static final String BIBTEXKEY = "bibtexkey"; - private static final String JOURNAL = "journal"; - private static final String TITLE = "title"; - private static final String YEAR = "year"; - - private static final String AUTHOR_TIP = "Multiple authors separated with 'and', e.g. author1 and author2"; - private static final String BIBTEXKEY_TIP = "[First author'last name][Article year] e.g. Jones2020"; - private static final String JOURNAL_TIP = "The name of the journal"; - private static final String TITLE_TIP = "The title of the article"; - private static final String YEAR_TIP = "The year of publication, e.g. 2005"; public FieldNameLabel(Field field, String name) { super(field.getDisplayName()); @@ -33,37 +23,18 @@ public FieldNameLabel(Field field, String name) { public void setTip(String name){ tip = new Tooltip(); - - switch (name){ - case AUTHOR: - { - tip.setText(AUTHOR_TIP); - break; - } - case BIBTEXKEY: - { - tip.setText(BIBTEXKEY_TIP); - break; - } - case JOURNAL: - { - tip.setText(JOURNAL_TIP); - break; - } - case TITLE: - { - tip.setText(TITLE_TIP); - break; - } - case YEAR: - { - tip.setText(YEAR_TIP); - break; - } - - default:{ - return; - } + if (StandardField.AUTHOR.getName().equals(name)) { + tip.setText(Localization.lang("AUTHOR_TIP")); + } else if (StandardField.BIBTEXKEY.getName().equals(name)) { + tip.setText(Localization.lang("BIBTEXKEY_TIP")); + } else if (StandardField.JOURNAL.getName().equals(name)) { + tip.setText(Localization.lang("JOURNAL_TIP")); + } else if (StandardField.TITLE.getName().equals(name)) { + tip.setText(Localization.lang("TITLE_TIP")); + } else if (StandardField.YEAR.getName().equals(name)) { + tip.setText(Localization.lang("YEAR_TIP")); + } else { + return; } this.setTooltip(tip); } diff --git a/src/main/java/org/jabref/model/entry/field/StandardField.java b/src/main/java/org/jabref/model/entry/field/StandardField.java index 6fe1b1e5a8f..f0beda058b6 100644 --- a/src/main/java/org/jabref/model/entry/field/StandardField.java +++ b/src/main/java/org/jabref/model/entry/field/StandardField.java @@ -21,6 +21,7 @@ public enum StandardField implements Field { ARCHIVEPREFIX("archiveprefix"), ASSIGNEE("assignee", FieldProperty.PERSON_NAMES), AUTHOR("author", FieldProperty.PERSON_NAMES), + BIBTEXKEY("bibtexkey"), BOOKAUTHOR("bookauthor", FieldProperty.PERSON_NAMES), BOOKPAGINATION("bookpagination", FieldProperty.PAGINATION), BOOKSUBTITLE("booksubtitle", FieldProperty.BOOK_NAME), @@ -164,9 +165,7 @@ public Set getProperties() { } @Override - public String getName() { - return name; - } + public String getName() { return name; } @Override public boolean isStandardField() { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 576ec67ee0d..a00d4bd4798 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2117,3 +2117,9 @@ Entry\ type\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Entry type cannot be em Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please enter a name. Shared\ database=Shared database Lookup=Lookup + +AUTHOR_TIP=Multiple authors separated with 'and', e.g. author1 and author2 +BIBTEXKEY_TIP =[First author'last name][Article year] e.g. Jones2020 +JOURNAL_TIP=The name of the journal +TITLE_TIP=The title of the article +YEAR_TIP=The year of publication, e.g. 2005 From 336b8c988270e267d61cb282f9f6730e3d44a412 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Wed, 4 Mar 2020 17:52:26 -0800 Subject: [PATCH 03/17] Update StandardField.java --- src/main/java/org/jabref/model/entry/field/StandardField.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/model/entry/field/StandardField.java b/src/main/java/org/jabref/model/entry/field/StandardField.java index f0beda058b6..59f623e5087 100644 --- a/src/main/java/org/jabref/model/entry/field/StandardField.java +++ b/src/main/java/org/jabref/model/entry/field/StandardField.java @@ -165,7 +165,9 @@ public Set getProperties() { } @Override - public String getName() { return name; } + public String getName() { + return name; + } @Override public boolean isStandardField() { From efc9ed31db8e2958bd64285e999c070e13a09888 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Wed, 4 Mar 2020 18:06:36 -0800 Subject: [PATCH 04/17] Update FieldNameLabel.java --- src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index 1cae5d8ac09..db51bcf2aae 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -21,7 +21,7 @@ public FieldNameLabel(Field field, String name) { setTip(name); } - public void setTip(String name){ + public void setTip(String name) { tip = new Tooltip(); if (StandardField.AUTHOR.getName().equals(name)) { tip.setText(Localization.lang("AUTHOR_TIP")); From 7b38f6adb2bf7c42fdcf9e932849c2038c26da72 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Wed, 4 Mar 2020 18:17:45 -0800 Subject: [PATCH 05/17] Code style update --- .../org/jabref/gui/fieldeditors/FieldNameLabel.java | 10 +++++----- src/main/resources/l10n/JabRef_en.properties | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index db51bcf2aae..e4e0812caf9 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -24,15 +24,15 @@ public FieldNameLabel(Field field, String name) { public void setTip(String name) { tip = new Tooltip(); if (StandardField.AUTHOR.getName().equals(name)) { - tip.setText(Localization.lang("AUTHOR_TIP")); + tip.setText(Localization.lang("AUTHOR TIP")); } else if (StandardField.BIBTEXKEY.getName().equals(name)) { - tip.setText(Localization.lang("BIBTEXKEY_TIP")); + tip.setText(Localization.lang("BIBTEXKEY TIP")); } else if (StandardField.JOURNAL.getName().equals(name)) { - tip.setText(Localization.lang("JOURNAL_TIP")); + tip.setText(Localization.lang("JOURNAL TIP")); } else if (StandardField.TITLE.getName().equals(name)) { - tip.setText(Localization.lang("TITLE_TIP")); + tip.setText(Localization.lang("TITLE TIP")); } else if (StandardField.YEAR.getName().equals(name)) { - tip.setText(Localization.lang("YEAR_TIP")); + tip.setText(Localization.lang("YEAR TIP")); } else { return; } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index a00d4bd4798..7cfbe1e7dac 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2118,8 +2118,8 @@ Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please Shared\ database=Shared database Lookup=Lookup -AUTHOR_TIP=Multiple authors separated with 'and', e.g. author1 and author2 -BIBTEXKEY_TIP =[First author'last name][Article year] e.g. Jones2020 -JOURNAL_TIP=The name of the journal -TITLE_TIP=The title of the article -YEAR_TIP=The year of publication, e.g. 2005 +AUTHOR\ TIP=Multiple authors separated with 'and', e.g. author1 and author2 +BIBTEXKEY\ TIP =[First author'last name][Article year] e.g. Jones2020 +JOURNAL\ TIP=The name of the journal +TITLE\ TIP=The title of the article +YEAR\ TIP=The year of publication, e.g. 2005 From 5876d284f32a9ee8c5070cb4ff3457a939e5d61f Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Wed, 4 Mar 2020 18:33:20 -0800 Subject: [PATCH 06/17] update Localization --- .../org/jabref/gui/fieldeditors/FieldNameLabel.java | 13 ++++++------- src/main/resources/l10n/JabRef_en.properties | 10 +++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index e4e0812caf9..1d6a916b471 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -10,7 +10,6 @@ import org.jabref.model.entry.field.StandardField; public class FieldNameLabel extends Label { - private Tooltip tip; public FieldNameLabel(Field field, String name) { super(field.getDisplayName()); @@ -22,17 +21,17 @@ public FieldNameLabel(Field field, String name) { } public void setTip(String name) { - tip = new Tooltip(); + Tooltip tip = new Tooltip(); if (StandardField.AUTHOR.getName().equals(name)) { - tip.setText(Localization.lang("AUTHOR TIP")); + tip.setText(Localization.lang("Multiple authors separated with 'and', e.g. author1 and author2")); } else if (StandardField.BIBTEXKEY.getName().equals(name)) { - tip.setText(Localization.lang("BIBTEXKEY TIP")); + tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); } else if (StandardField.JOURNAL.getName().equals(name)) { - tip.setText(Localization.lang("JOURNAL TIP")); + tip.setText(Localization.lang("The name of the journal")); } else if (StandardField.TITLE.getName().equals(name)) { - tip.setText(Localization.lang("TITLE TIP")); + tip.setText(Localization.lang("The title of the article")); } else if (StandardField.YEAR.getName().equals(name)) { - tip.setText(Localization.lang("YEAR TIP")); + tip.setText(Localization.lang("The year of publication, e.g. 2005")); } else { return; } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 7cfbe1e7dac..059c2515f6b 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2118,8 +2118,8 @@ Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please Shared\ database=Shared database Lookup=Lookup -AUTHOR\ TIP=Multiple authors separated with 'and', e.g. author1 and author2 -BIBTEXKEY\ TIP =[First author'last name][Article year] e.g. Jones2020 -JOURNAL\ TIP=The name of the journal -TITLE\ TIP=The title of the article -YEAR\ TIP=The year of publication, e.g. 2005 +Multiple\ authors\ separated\ with\ 'and',\ e.g.\ author1\ and\ author2=Multiple authors separated with 'and', e.g. author1 and author2 +[First\ author'last\ name][Article\ year]\ e.g.\ Jones2020=[First author'last name][Article year] e.g. Jones2020 +The\ name\ of\ the\ journal=The name of the journal +The\ title\ of\ the\ article=The title of the article +The\ year\ of\ publication,\ e.g.\ 2005=The year of publication, e.g. 2005 From 034679588deaf1ba2707a06f101a5d549cdf3018 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Thu, 5 Mar 2020 19:30:45 -0800 Subject: [PATCH 07/17] Localization updated --- .../gui/entryeditor/FieldsEditorTab.java | 2 +- .../gui/fieldeditors/FieldNameLabel.java | 24 +++++++++---------- src/main/resources/l10n/JabRef_en.properties | 9 +++---- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 4a2db4f4087..603450b39ce 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -97,7 +97,7 @@ private void setupPanel(BibEntry entry, boolean compressed) { fieldEditor.bindToEntry(entry); editors.put(field, fieldEditor); - labels.add(new FieldNameLabel(field, field.getName())); + labels.add(new FieldNameLabel(field)); } ColumnConstraints columnExpand = new ColumnConstraints(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index 1d6a916b471..cf7a4a0f9d8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -11,27 +11,27 @@ public class FieldNameLabel extends Label { - public FieldNameLabel(Field field, String name) { + public FieldNameLabel(Field field) { super(field.getDisplayName()); setPadding(new Insets(4, 0, 0, 0)); setAlignment(Pos.CENTER); setPrefHeight(Double.POSITIVE_INFINITY); - setTip(name); + setTip(field); } - public void setTip(String name) { + public void setTip(Field field) { Tooltip tip = new Tooltip(); - if (StandardField.AUTHOR.getName().equals(name)) { - tip.setText(Localization.lang("Multiple authors separated with 'and', e.g. author1 and author2")); - } else if (StandardField.BIBTEXKEY.getName().equals(name)) { + if (StandardField.AUTHOR.getName().equals(field.getName())) { + tip.setText(Localization.lang("The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas.")); + } else if (StandardField.BIBTEXKEY.getName().equals(field.getName())) { tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); - } else if (StandardField.JOURNAL.getName().equals(name)) { - tip.setText(Localization.lang("The name of the journal")); - } else if (StandardField.TITLE.getName().equals(name)) { - tip.setText(Localization.lang("The title of the article")); - } else if (StandardField.YEAR.getName().equals(name)) { - tip.setText(Localization.lang("The year of publication, e.g. 2005")); + } else if (StandardField.JOURNAL.getName().equals(field.getName())) { + tip.setText(Localization.lang("Journal name. Abbrevations could be found in 'http://abbrv.jabref.org/journals/'")); + } else if (StandardField.TITLE.getName().equals(field.getName())) { + tip.setText(Localization.lang("The work's title")); + } else if (StandardField.YEAR.getName().equals(field.getName())) { + tip.setText(Localization.lang("The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as ‘(about 1984)’.")); } else { return; } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 059c2515f6b..fd195bc076a 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2118,8 +2118,9 @@ Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please Shared\ database=Shared database Lookup=Lookup -Multiple\ authors\ separated\ with\ 'and',\ e.g.\ author1\ and\ author2=Multiple authors separated with 'and', e.g. author1 and author2 +The\ name(s)\ of\ the\ author(s),\ in\ the\ format\ described\ in\ the\ LATEX\ book.\ Remember,\ all\ names\ are\ separated\ with\ the\ and\ keyword,\ and\ not\ commas.=The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas. +Journal\ name.\ Abbrevations\ could\ be\ found\ in\ http://abbrv.jabref.org/journals/=Journal name. Abbrevations could be found in http://abbrv.jabref.org/journals/ [First\ author'last\ name][Article\ year]\ e.g.\ Jones2020=[First author'last name][Article year] e.g. Jones2020 -The\ name\ of\ the\ journal=The name of the journal -The\ title\ of\ the\ article=The title of the article -The\ year\ of\ publication,\ e.g.\ 2005=The year of publication, e.g. 2005 +The\ work's\ title=The work's title +The\ year\ of\ publication\ or,\ for\ an\ unpublished\ work,\ the\ year\ it\ was\ written.\ Generally\ it\ should\ consist\ of\ four\ numerals,\ such\ as\ 1984,\ although\ the\ standard\ styles\ can\ handle\ any\ year\ whose\ last\ four\ nonpunctuation\ characters\ are\ numerals,\ such\ as\ (about\ 1984).=The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as (about 1984). + From 49ad3a21f3dffdf5c08101acfb26fdc8b9915f17 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Thu, 5 Mar 2020 19:43:24 -0800 Subject: [PATCH 08/17] Localization updated --- src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java | 2 +- src/main/resources/l10n/JabRef_en.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index cf7a4a0f9d8..588d1e6a1bf 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -27,7 +27,7 @@ public void setTip(Field field) { } else if (StandardField.BIBTEXKEY.getName().equals(field.getName())) { tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); } else if (StandardField.JOURNAL.getName().equals(field.getName())) { - tip.setText(Localization.lang("Journal name. Abbrevations could be found in 'http://abbrv.jabref.org/journals/'")); + tip.setText(Localization.lang("Journal name. Abbrevations could be used.")); } else if (StandardField.TITLE.getName().equals(field.getName())) { tip.setText(Localization.lang("The work's title")); } else if (StandardField.YEAR.getName().equals(field.getName())) { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index fd195bc076a..1c84f49960f 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2119,7 +2119,7 @@ Shared\ database=Shared database Lookup=Lookup The\ name(s)\ of\ the\ author(s),\ in\ the\ format\ described\ in\ the\ LATEX\ book.\ Remember,\ all\ names\ are\ separated\ with\ the\ and\ keyword,\ and\ not\ commas.=The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas. -Journal\ name.\ Abbrevations\ could\ be\ found\ in\ http://abbrv.jabref.org/journals/=Journal name. Abbrevations could be found in http://abbrv.jabref.org/journals/ +Journal\ name.\ Abbrevations\ could\ be\ used.=Journal name. Abbrevations could be used. [First\ author'last\ name][Article\ year]\ e.g.\ Jones2020=[First author'last name][Article year] e.g. Jones2020 The\ work's\ title=The work's title The\ year\ of\ publication\ or,\ for\ an\ unpublished\ work,\ the\ year\ it\ was\ written.\ Generally\ it\ should\ consist\ of\ four\ numerals,\ such\ as\ 1984,\ although\ the\ standard\ styles\ can\ handle\ any\ year\ whose\ last\ four\ nonpunctuation\ characters\ are\ numerals,\ such\ as\ (about\ 1984).=The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as (about 1984). From 4e8c29fee36f74567ad8b59750e020357729dc29 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Thu, 5 Mar 2020 19:49:40 -0800 Subject: [PATCH 09/17] Update JabRef_en.properties --- src/main/resources/l10n/JabRef_en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 1c84f49960f..11f42c60b91 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2122,5 +2122,5 @@ The\ name(s)\ of\ the\ author(s),\ in\ the\ format\ described\ in\ the\ LATEX\ b Journal\ name.\ Abbrevations\ could\ be\ used.=Journal name. Abbrevations could be used. [First\ author'last\ name][Article\ year]\ e.g.\ Jones2020=[First author'last name][Article year] e.g. Jones2020 The\ work's\ title=The work's title -The\ year\ of\ publication\ or,\ for\ an\ unpublished\ work,\ the\ year\ it\ was\ written.\ Generally\ it\ should\ consist\ of\ four\ numerals,\ such\ as\ 1984,\ although\ the\ standard\ styles\ can\ handle\ any\ year\ whose\ last\ four\ nonpunctuation\ characters\ are\ numerals,\ such\ as\ (about\ 1984).=The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as (about 1984). +The\ year\ of\ publication\ or,\ for\ an\ unpublished\ work,\ the\ year\ it\ was\ written.\ Generally\ it\ should\ consist\ of\ four\ numerals,\ such\ as\ 1984,\ although\ the\ standard\ styles\ can\ handle\ any\ year\ whose\ last\ four\ nonpunctuation\ characters\ are\ numerals,\ such\ as\ '(about\ 1984)'.=The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'. From a9ae44950469ca4a1bdfef20f8568edc1f690043 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Thu, 5 Mar 2020 20:05:27 -0800 Subject: [PATCH 10/17] Update FieldNameLabel.java --- src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index 588d1e6a1bf..68920951021 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -31,7 +31,7 @@ public void setTip(Field field) { } else if (StandardField.TITLE.getName().equals(field.getName())) { tip.setText(Localization.lang("The work's title")); } else if (StandardField.YEAR.getName().equals(field.getName())) { - tip.setText(Localization.lang("The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as ‘(about 1984)’.")); + tip.setText(Localization.lang("The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'.")); } else { return; } From 15d7b995c3f1b1a3ac5eefea1bd3f47d4138239a Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Fri, 6 Mar 2020 00:19:58 -0800 Subject: [PATCH 11/17] Update setTip() method in FieldNameLabel.java Change if statement to switch statement, which makes the code more understandable. --- .../gui/fieldeditors/FieldNameLabel.java | 41 +++++++++++++------ .../model/entry/field/StandardField.java | 1 - 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index 68920951021..cd5c996c2c1 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -7,6 +7,7 @@ import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.InternalField; import org.jabref.model.entry.field.StandardField; public class FieldNameLabel extends Label { @@ -22,18 +23,34 @@ public FieldNameLabel(Field field) { public void setTip(Field field) { Tooltip tip = new Tooltip(); - if (StandardField.AUTHOR.getName().equals(field.getName())) { - tip.setText(Localization.lang("The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas.")); - } else if (StandardField.BIBTEXKEY.getName().equals(field.getName())) { - tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); - } else if (StandardField.JOURNAL.getName().equals(field.getName())) { - tip.setText(Localization.lang("Journal name. Abbrevations could be used.")); - } else if (StandardField.TITLE.getName().equals(field.getName())) { - tip.setText(Localization.lang("The work's title")); - } else if (StandardField.YEAR.getName().equals(field.getName())) { - tip.setText(Localization.lang("The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'.")); - } else { - return; + if (field.isStandardField()) { + StandardField standardField = (StandardField)field; + switch (standardField) { + case AUTHOR: + tip.setText(Localization.lang("The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas.")); + break; + case JOURNAL: + tip.setText(Localization.lang("Journal name. Abbrevations could be used.")); + break; + case TITLE: + tip.setText(Localization.lang("The work's title")); + break; + case YEAR: + tip.setText(Localization.lang("The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'.")); + break; + default: + return; + } + } + else if (field instanceof InternalField) { + InternalField InternalField = (InternalField) field; + switch (InternalField) { + case KEY_FIELD: + tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); + break; + default: + return; + } } this.setTooltip(tip); } diff --git a/src/main/java/org/jabref/model/entry/field/StandardField.java b/src/main/java/org/jabref/model/entry/field/StandardField.java index 59f623e5087..6fe1b1e5a8f 100644 --- a/src/main/java/org/jabref/model/entry/field/StandardField.java +++ b/src/main/java/org/jabref/model/entry/field/StandardField.java @@ -21,7 +21,6 @@ public enum StandardField implements Field { ARCHIVEPREFIX("archiveprefix"), ASSIGNEE("assignee", FieldProperty.PERSON_NAMES), AUTHOR("author", FieldProperty.PERSON_NAMES), - BIBTEXKEY("bibtexkey"), BOOKAUTHOR("bookauthor", FieldProperty.PERSON_NAMES), BOOKPAGINATION("bookpagination", FieldProperty.PAGINATION), BOOKSUBTITLE("booksubtitle", FieldProperty.BOOK_NAME), From f3611a2e5fc4e903beaccf60ad62188aee9e8e8b Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Fri, 6 Mar 2020 00:22:29 -0800 Subject: [PATCH 12/17] Update FieldNameLabel.java --- src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index cd5c996c2c1..ee55e796216 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -43,8 +43,8 @@ public void setTip(Field field) { } } else if (field instanceof InternalField) { - InternalField InternalField = (InternalField) field; - switch (InternalField) { + InternalField internalField = (InternalField) field; + switch (internalField) { case KEY_FIELD: tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); break; From a6ec7aadfbb95243e90788237d1aa3cb553353b2 Mon Sep 17 00:00:00 2001 From: ShikunXiong Date: Fri, 6 Mar 2020 02:42:05 -0800 Subject: [PATCH 13/17] Codestyle problems fixed --- .../org/jabref/gui/fieldeditors/FieldNameLabel.java | 11 +++++------ src/main/resources/l10n/JabRef_en.properties | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index ee55e796216..bf68554cdca 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -24,13 +24,13 @@ public FieldNameLabel(Field field) { public void setTip(Field field) { Tooltip tip = new Tooltip(); if (field.isStandardField()) { - StandardField standardField = (StandardField)field; + StandardField standardField = (StandardField) field; switch (standardField) { case AUTHOR: - tip.setText(Localization.lang("The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas.")); + tip.setText(Localization.lang("The name(s) of the author(s), in the format described in the LaTeX book. Remember, all names are separated with the and keyword, and not commas.")); break; case JOURNAL: - tip.setText(Localization.lang("Journal name. Abbrevations could be used.")); + tip.setText(Localization.lang("Journal name. Abbrevations may be used.")); break; case TITLE: tip.setText(Localization.lang("The work's title")); @@ -41,12 +41,11 @@ public void setTip(Field field) { default: return; } - } - else if (field instanceof InternalField) { + } else if (field instanceof InternalField) { InternalField internalField = (InternalField) field; switch (internalField) { case KEY_FIELD: - tip.setText(Localization.lang("[First author'last name][Article year] e.g. Jones2020")); + tip.setText(Localization.lang("[First author'last name][Article year], e.g. Jones2020")); break; default: return; diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 11f42c60b91..a766149648f 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2118,9 +2118,9 @@ Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please Shared\ database=Shared database Lookup=Lookup -The\ name(s)\ of\ the\ author(s),\ in\ the\ format\ described\ in\ the\ LATEX\ book.\ Remember,\ all\ names\ are\ separated\ with\ the\ and\ keyword,\ and\ not\ commas.=The name(s) of the author(s), in the format described in the LATEX book. Remember, all names are separated with the and keyword, and not commas. -Journal\ name.\ Abbrevations\ could\ be\ used.=Journal name. Abbrevations could be used. -[First\ author'last\ name][Article\ year]\ e.g.\ Jones2020=[First author'last name][Article year] e.g. Jones2020 +The\ name(s)\ of\ the\ author(s),\ in\ the\ format\ described\ in\ the\ LaTeX\ book.\ Remember,\ all\ names\ are\ separated\ with\ the\ and\ keyword,\ and\ not\ commas.=The name(s) of the author(s), in the format described in the LaTeX book. Remember, all names are separated with the and keyword, and not commas. +Journal\ name.\ Abbrevations\ may\ be\ used.=Journal name. Abbrevations may be used. +[First\ author'last\ name][Article\ year],\ e.g.\ Jones2020=[First author'last name][Article year], e.g. Jones2020 The\ work's\ title=The work's title The\ year\ of\ publication\ or,\ for\ an\ unpublished\ work,\ the\ year\ it\ was\ written.\ Generally\ it\ should\ consist\ of\ four\ numerals,\ such\ as\ 1984,\ although\ the\ standard\ styles\ can\ handle\ any\ year\ whose\ last\ four\ nonpunctuation\ characters\ are\ numerals,\ such\ as\ '(about\ 1984)'.=The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'. From 67790bcaadd4b288e392ecb39f2a27a980763c69 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Fri, 20 Mar 2020 13:53:18 +0100 Subject: [PATCH 14/17] Add descriptions for more fields --- .../gui/fieldeditors/FieldNameLabel.java | 141 +++++++++++++++--- .../model/entry/field/StandardField.java | 2 + src/main/resources/l10n/JabRef_en.properties | 68 ++++++++- 3 files changed, 185 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java index bf68554cdca..91b48f52721 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldNameLabel.java @@ -9,6 +9,7 @@ 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.strings.StringUtil; public class FieldNameLabel extends Label { @@ -18,39 +19,143 @@ public FieldNameLabel(Field field) { setPadding(new Insets(4, 0, 0, 0)); setAlignment(Pos.CENTER); setPrefHeight(Double.POSITIVE_INFINITY); - setTip(field); + + String description = getDescription(field); + if (StringUtil.isNotBlank(description)) { + Tooltip tooltip = new Tooltip(); + tooltip.setText(description); + this.setTooltip(tooltip); + } } - public void setTip(Field field) { - Tooltip tip = new Tooltip(); + public String getDescription(Field field) { if (field.isStandardField()) { StandardField standardField = (StandardField) field; switch (standardField) { + case ADDENDUM: + return Localization.lang("Miscellaneous bibliographic data usually printed at the end of the entry."); + case AFTERWORD: + return Localization.lang("Author(s) of an afterword to the work."); + case ANNOTATOR: + return Localization.lang("Author(s) of annotations to the work."); case AUTHOR: - tip.setText(Localization.lang("The name(s) of the author(s), in the format described in the LaTeX book. Remember, all names are separated with the and keyword, and not commas.")); - break; + return Localization.lang("Author(s) of the work."); + case BOOKTITLE: + return Localization.lang("Title of the main publication this work is part of."); + case CHAPTER: + return Localization.lang("Chapter or section or any other unit of a work."); + case COMMENTATOR: + return Localization.lang("Author(s) of a commentary to the work.") + "\n" + + Localization.lang("Note that this field is intended for commented editions which have a commentator in addition to the author. If the work is a stand-alone commentary, the commentator should be given in the author field."); + case DATE: + return Localization.lang("Publication date of the work."); + case DOI: + return Localization.lang("Digital Object Identifier of the work."); + case EDITION: + return Localization.lang("Edition of a printed publication."); + case EDITOR: + return Localization.lang("Editor(s) of the work or the main publication, depending on the type of the entry."); + case EID: + return Localization.lang("Electronic identifier of a work.") + "\n" + + Localization.lang("This field may replace the pages field for journals deviating from the classic pagination scheme of printed journals by only enumerating articles or papers and not pages."); + case EPRINT: + return Localization.lang("Electronic identifier of an online publication.") + "\n" + + Localization.lang("This is roughly comparable to a DOI but specific to a certain archive, repository, service, or system."); + case EPRINTCLASS: + case PRIMARYCLASS: + return Localization.lang("Additional information related to the resource indicated by the eprint field.") + "\n" + + Localization.lang("This could be a section of an archive, a path indicating a service, a classification of some sort."); + case EPRINTTYPE: + case ARCHIVEPREFIX: + return Localization.lang("Type of the eprint identifier, e. g., the name of the archive, repository, service, or system the eprint field refers to."); + case EVENTDATE: + return Localization.lang("Date of a conference, a symposium, or some other event."); + case EVENTTITLE: + return Localization.lang("Title of a conference, a symposium, or some other event.") + "\n" + + Localization.lang("Note that this field holds the plain title of the event. Things like \"Proceedings of the Fifth XYZ Conference\" go into the titleaddon or booktitleaddon field."); + case EVENTTITLEADDON: + return Localization.lang("Annex to the eventtitle field.") + "\n" + + Localization.lang("Can be used for known event acronyms."); + case FILE: + case PDF: + return Localization.lang("Link(s) to a local PDF or other document of the work."); + case HOWPUBLISHED: + return Localization.lang("Publication notice for unusual publications which do not fit into any of the common categories."); + case INSTITUTION: + case SCHOOL: + return Localization.lang("Name of a university or some other institution."); + case INTRODUCTION: + return Localization.lang("Author(s) of an introduction to the work."); + case ISBN: + return Localization.lang("International Standard Book Number of a book."); + case ISSUE: + return Localization.lang("Issue of a journal.") + "\n" + + Localization.lang("This field is intended for journals whose individual issues are identified by a designation such as \"Spring\" or \"Summer\" rather than the month or a number. Integer ranges and short designators are better written to the number field."); + case ISSUESUBTITLE: + return Localization.lang("Subtitle of a specific issue of a journal or other periodical."); + case ISSUETITLE: + return Localization.lang("Title of a specific issue of a journal or other periodical."); + case JOURNALTITLE: case JOURNAL: - tip.setText(Localization.lang("Journal name. Abbrevations may be used.")); - break; + return Localization.lang("Name of a journal, a newspaper, or some other periodical."); + case LABEL: + return Localization.lang("Designation to be used by the citation style as a substitute for the regular label if any data required to generate the regular label is missing."); + case LIBRARY: + return Localization.lang("Information such as a library name and a call number."); + case LOCATION: + case ADDRESS: + return Localization.lang("The place(s) of publication, i. e., the location of the publisher or institution, depending on the entry type."); + case MAINTITLE: + return Localization.lang("Main title of a multi-volume book, such as \"Collected Works\"."); + case MONTH: + return Localization.lang("Publication month."); + case NOTE: + return Localization.lang("Miscellaneous bibliographic data which does not fit into any other field."); + case NUMBER: + return Localization.lang("Number of a journal or the volume/number of a book in a series."); + case ORGANIZATION: + return Localization.lang("Organization(s) that published a manual or an online resource, or sponsored a conference."); + case PAGES: + return Localization.lang("One or more page numbers or page ranges.") + "\n" + + Localization.lang("If the work is published as part of another one, such as an article in a journal or a collection, this field holds the relevant page range in that other work. It may also be used to limit the reference to a specific part of a work (a chapter in a book, for example). For papers in electronic journals with anon-classical pagination setup the eid field may be more suitable."); + case PAGETOTAL: + return Localization.lang("Total number of pages of the work."); + case PUBLISHER: + return Localization.lang("Name(s) of the publisher(s)."); + case PUBSTATE: + return Localization.lang("Publication state of the work, e. g., \"in press\"."); + case SERIES: + return Localization.lang("Name of a publication series, such as \"Studies in...\", or the number of a journal series."); + case SUBTITLE: + return Localization.lang("Subtitle of the work."); case TITLE: - tip.setText(Localization.lang("The work's title")); - break; + return Localization.lang("Title of the work."); + case URL: + return Localization.lang("URL of an online publication."); + case URLDATE: + return Localization.lang("Access date of the address specified in the url field."); + case VENUE: + return Localization.lang("Location of a conference, a symposium, or some other event."); + case VERSION: + return Localization.lang("Revision number of a piece of software, a manual, etc."); + case VOLUME: + return Localization.lang("Volume of a multi-volume book or a periodical."); + case VOLUMES: + return Localization.lang("Total number of volumes of a multi-volume work."); case YEAR: - tip.setText(Localization.lang("The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'.")); - break; - default: - return; + return Localization.lang("Year of publication."); + case KEYWORDS: + return Localization.lang("Separated list of keywords."); } } else if (field instanceof InternalField) { InternalField internalField = (InternalField) field; switch (internalField) { case KEY_FIELD: - tip.setText(Localization.lang("[First author'last name][Article year], e.g. Jones2020")); - break; - default: - return; + return Localization.lang("Key by which the work may be cited."); + case GROUPS: + return Localization.lang("Name(s) of the (manual) groups the entry belongs to."); } } - this.setTooltip(tip); + return ""; } } diff --git a/src/main/java/org/jabref/model/entry/field/StandardField.java b/src/main/java/org/jabref/model/entry/field/StandardField.java index 6fe1b1e5a8f..6f10d4e0c57 100644 --- a/src/main/java/org/jabref/model/entry/field/StandardField.java +++ b/src/main/java/org/jabref/model/entry/field/StandardField.java @@ -72,6 +72,8 @@ public enum StandardField implements Field { KEY("key"), KEYWORDS("keywords"), LANGUAGE("language", FieldProperty.LANGUAGE), + LABEL("label"), + LIBRARY("library"), LOCATION("location"), MAINSUBTITLE("mainsubtitle", FieldProperty.BOOK_NAME), MAINTITLE("maintitle", FieldProperty.BOOK_NAME), diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index a766149648f..357e091edf5 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2110,17 +2110,69 @@ Required\ and\ optional\ fields=Required and optional fields Index=Index Remove\ entry\ type=Remove entry type Remove\ field\ %0\ from\ currently\ selected\ entry\ type=Remove field %0 from currently selected entry type - Optional=Optional Required=Required Entry\ type\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Entry type cannot be empty. Please enter a name. Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please enter a name. Shared\ database=Shared database Lookup=Lookup - -The\ name(s)\ of\ the\ author(s),\ in\ the\ format\ described\ in\ the\ LaTeX\ book.\ Remember,\ all\ names\ are\ separated\ with\ the\ and\ keyword,\ and\ not\ commas.=The name(s) of the author(s), in the format described in the LaTeX book. Remember, all names are separated with the and keyword, and not commas. -Journal\ name.\ Abbrevations\ may\ be\ used.=Journal name. Abbrevations may be used. -[First\ author'last\ name][Article\ year],\ e.g.\ Jones2020=[First author'last name][Article year], e.g. Jones2020 -The\ work's\ title=The work's title -The\ year\ of\ publication\ or,\ for\ an\ unpublished\ work,\ the\ year\ it\ was\ written.\ Generally\ it\ should\ consist\ of\ four\ numerals,\ such\ as\ 1984,\ although\ the\ standard\ styles\ can\ handle\ any\ year\ whose\ last\ four\ nonpunctuation\ characters\ are\ numerals,\ such\ as\ '(about\ 1984)'.=The year of publication or, for an unpublished work, the year it was written. Generally it should consist of four numerals, such as 1984, although the standard styles can handle any year whose last four nonpunctuation characters are numerals, such as '(about 1984)'. - +Access\ date\ of\ the\ address\ specified\ in\ the\ url\ field.=Access date of the address specified in the url field. +Additional\ information\ related\ to\ the\ resource\ indicated\ by\ the\ eprint\ field.=Additional information related to the resource indicated by the eprint field. +Annex\ to\ the\ eventtitle\ field.=Annex to the eventtitle field. +Author(s)\ of\ a\ commentary\ to\ the\ work.=Author(s) of a commentary to the work. +Author(s)\ of\ an\ afterword\ to\ the\ work.=Author(s) of an afterword to the work. +Author(s)\ of\ an\ introduction\ to\ the\ work.=Author(s) of an introduction to the work. +Author(s)\ of\ annotations\ to\ the\ work.=Author(s) of annotations to the work. +Author(s)\ of\ the\ work.=Author(s) of the work. +Can\ be\ used\ for\ known\ event\ acronyms.=Can be used for known event acronyms. +Chapter\ or\ section\ or\ any\ other\ unit\ of\ a\ work.=Chapter or section or any other unit of a work. +Date\ of\ a\ conference,\ a\ symposium,\ or\ some\ other\ event.=Date of a conference, a symposium, or some other event. +Designation\ to\ be\ used\ by\ the\ citation\ style\ as\ a\ substitute\ for\ the\ regular\ label\ if\ any\ data\ required\ to\ generate\ the\ regular\ label\ is\ missing.=Designation to be used by the citation style as a substitute for the regular label if any data required to generate the regular label is missing. +Digital\ Object\ Identifier\ of\ the\ work.=Digital Object Identifier of the work. +Edition\ of\ a\ printed\ publication.=Edition of a printed publication. +Editor(s)\ of\ the\ work\ or\ the\ main\ publication,\ depending\ on\ the\ type\ of\ the\ entry.=Editor(s) of the work or the main publication, depending on the type of the entry. +Electronic\ identifier\ of\ a\ work.=Electronic identifier of a work. +Electronic\ identifier\ of\ an\ online\ publication.=Electronic identifier of an online publication. +If\ the\ work\ is\ published\ as\ part\ of\ another\ one,\ such\ as\ an\ article\ in\ a\ journal\ or\ a\ collection,\ this\ field\ holds\ the\ relevant\ page\ range\ in\ that\ other\ work.\ It\ may\ also\ be\ used\ to\ limit\ the\ reference\ to\ a\ specific\ part\ of\ a\ work\ (a\ chapter\ in\ a\ book,\ for\ example).\ For\ papers\ in\ electronic\ journals\ with\ anon-classical\ pagination\ setup\ the\ eid\ field\ may\ be\ more\ suitable.=If the work is published as part of another one, such as an article in a journal or a collection, this field holds the relevant page range in that other work. It may also be used to limit the reference to a specific part of a work (a chapter in a book, for example). For papers in electronic journals with anon-classical pagination setup the eid field may be more suitable. +Information\ such\ as\ a\ library\ name\ and\ a\ call\ number.=Information such as a library name and a call number. +International\ Standard\ Book\ Number\ of\ a\ book.=International Standard Book Number of a book. +Issue\ of\ a\ journal.=Issue of a journal. +Key\ by\ which\ the\ work\ may\ be\ cited.=Key by which the work may be cited. +Link(s)\ to\ a\ local\ PDF\ or\ other\ document\ of\ the\ work.=Link(s) to a local PDF or other document of the work. +Location\ of\ a\ conference,\ a\ symposium,\ or\ some\ other\ event.=Location of a conference, a symposium, or some other event. +Main\ title\ of\ a\ multi-volume\ book,\ such\ as\ "Collected\ Works".=Main title of a multi-volume book, such as "Collected Works". +Miscellaneous\ bibliographic\ data\ usually\ printed\ at\ the\ end\ of\ the\ entry.=Miscellaneous bibliographic data usually printed at the end of the entry. +Miscellaneous\ bibliographic\ data\ which\ does\ not\ fit\ into\ any\ other\ field.=Miscellaneous bibliographic data which does not fit into any other field. +Name(s)\ of\ the\ (manual)\ groups\ the\ entry\ belongs\ to.=Name(s) of the (manual) groups the entry belongs to. +Name(s)\ of\ the\ publisher(s).=Name(s) of the publisher(s). +Name\ of\ a\ journal,\ a\ newspaper,\ or\ some\ other\ periodical.=Name of a journal, a newspaper, or some other periodical. +Name\ of\ a\ publication\ series,\ such\ as\ "Studies\ in...",\ or\ the\ number\ of\ a\ journal\ series.=Name of a publication series, such as "Studies in...", or the number of a journal series. +Name\ of\ a\ university\ or\ some\ other\ institution.=Name of a university or some other institution. +Note\ that\ this\ field\ holds\ the\ plain\ title\ of\ the\ event.\ Things\ like\ "Proceedings\ of\ the\ Fifth\ XYZ\ Conference"\ go\ into\ the\ titleaddon\ or\ booktitleaddon\ field.=Note that this field holds the plain title of the event. Things like "Proceedings of the Fifth XYZ Conference" go into the titleaddon or booktitleaddon field. +Note\ that\ this\ field\ is\ intended\ for\ commented\ editions\ which\ have\ a\ commentator\ in\ addition\ to\ the\ author.\ If\ the\ work\ is\ a\ stand-alone\ commentary,\ the\ commentator\ should\ be\ given\ in\ the\ author\ field.=Note that this field is intended for commented editions which have a commentator in addition to the author. If the work is a stand-alone commentary, the commentator should be given in the author field. +Number\ of\ a\ journal\ or\ the\ volume/number\ of\ a\ book\ in\ a\ series.=Number of a journal or the volume/number of a book in a series. +One\ or\ more\ page\ numbers\ or\ page\ ranges.=One or more page numbers or page ranges. +Organization(s)\ that\ published\ a\ manual\ or\ an\ online\ resource,\ or\ sponsored\ a\ conference.=Organization(s) that published a manual or an online resource, or sponsored a conference. +Publication\ date\ of\ the\ work.=Publication date of the work. +Publication\ month.=Publication month. +Publication\ notice\ for\ unusual\ publications\ which\ do\ not\ fit\ into\ any\ of\ the\ common\ categories.=Publication notice for unusual publications which do not fit into any of the common categories. +Publication\ state\ of\ the\ work,\ e.\ g.,\ "in\ press".=Publication state of the work, e. g., "in press". +Revision\ number\ of\ a\ piece\ of\ software,\ a\ manual,\ etc.=Revision number of a piece of software, a manual, etc. +Separated\ list\ of\ keywords.=Separated list of keywords. +Subtitle\ of\ a\ specific\ issue\ of\ a\ journal\ or\ other\ periodical.=Subtitle of a specific issue of a journal or other periodical. +Subtitle\ of\ the\ work.=Subtitle of the work. +The\ place(s)\ of\ publication,\ i.\ e.,\ the\ location\ of\ the\ publisher\ or\ institution,\ depending\ on\ the\ entry\ type.=The place(s) of publication, i. e., the location of the publisher or institution, depending on the entry type. +This\ could\ be\ a\ section\ of\ an\ archive,\ a\ path\ indicating\ a\ service,\ a\ classification\ of\ some\ sort.=This could be a section of an archive, a path indicating a service, a classification of some sort. +This\ field\ is\ intended\ for\ journals\ whose\ individual\ issues\ are\ identified\ by\ a\ designation\ such\ as\ "Spring"\ or\ "Summer"\ rather\ than\ the\ month\ or\ a\ number.\ Integer\ ranges\ and\ short\ designators\ are\ better\ written\ to\ the\ number\ field.=This field is intended for journals whose individual issues are identified by a designation such as "Spring" or "Summer" rather than the month or a number. Integer ranges and short designators are better written to the number field. +This\ field\ may\ replace\ the\ pages\ field\ for\ journals\ deviating\ from\ the\ classic\ pagination\ scheme\ of\ printed\ journals\ by\ only\ enumerating\ articles\ or\ papers\ and\ not\ pages.=This field may replace the pages field for journals deviating from the classic pagination scheme of printed journals by only enumerating articles or papers and not pages. +This\ is\ roughly\ comparable\ to\ a\ DOI\ but\ specific\ to\ a\ certain\ archive,\ repository,\ service,\ or\ system.=This is roughly comparable to a DOI but specific to a certain archive, repository, service, or system. +Title\ of\ a\ conference,\ a\ symposium,\ or\ some\ other\ event.=Title of a conference, a symposium, or some other event. +Title\ of\ a\ specific\ issue\ of\ a\ journal\ or\ other\ periodical.=Title of a specific issue of a journal or other periodical. +Title\ of\ the\ main\ publication\ this\ work\ is\ part\ of.=Title of the main publication this work is part of. +Title\ of\ the\ work.=Title of the work. +Total\ number\ of\ pages\ of\ the\ work.=Total number of pages of the work. +Total\ number\ of\ volumes\ of\ a\ multi-volume\ work.=Total number of volumes of a multi-volume work. +Type\ of\ the\ eprint\ identifier,\ e.\ g.,\ the\ name\ of\ the\ archive,\ repository,\ service,\ or\ system\ the\ eprint\ field\ refers\ to.=Type of the eprint identifier, e. g., the name of the archive, repository, service, or system the eprint field refers to. +URL\ of\ an\ online\ publication.=URL of an online publication. +Volume\ of\ a\ multi-volume\ book\ or\ a\ periodical.=Volume of a multi-volume book or a periodical. +Year\ of\ publication.=Year of publication. From b46c5be830ca00e5f292c4414b3dbd8ef9e62e9e Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Fri, 20 Mar 2020 13:56:10 +0100 Subject: [PATCH 15/17] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 497fcd787f9bca1dd281787220a9d849eb67d98f Author: Linus Dietz Date: Thu Mar 19 21:07:34 2020 +0100 Move Springer key to environment (#6139) commit 99183e1680fc7dfaa7b3b3f1628fa1331a041594 Author: Christoph Date: Wed Mar 18 20:28:36 2020 +0100 Add APS Fetcher (refactored) (#6143) * Add APS fetcher * Fix case sensitivity bug * Refactor ApsFetcher * Add note about APS fetcher * Refactor findFulltext() * Refactor getId() * Parameterize ApsFetcherTest * Add link to APS changelog entry * Refactor APS Fetcher * make separate tests Co-authored-by: August Janse commit edec608ab0562e7f09a493ea3c3c54b4cc5f82f6 Author: Siedlerchr Date: Wed Mar 18 14:47:28 2020 +0100 trigger build commit cd051a1f93dd8eb6d7a94ae76230ba1b4c32e908 Merge: 74408c054 a8abfcd2b Author: github actions Date: Tue Mar 17 18:08:19 2020 +0000 Merge commit 'a8abfcd2b0d94120ad32097aec74923ab1cb857f' commit a8abfcd2b0d94120ad32097aec74923ab1cb857f Author: github actions Date: Tue Mar 17 18:08:19 2020 +0000 Squashed 'src/main/resources/csl-styles/' changes from 268df9ebc7..db8bd334bd db8bd334bd Create journal-of-breast-cancer.csl (#4630) git-subtree-dir: src/main/resources/csl-styles git-subtree-split: db8bd334bd0a68f0a42a6e435e08216da3eaf0f6 commit 74408c054082dd0d4da2f20a8231f9a05c25c2bb Author: Linus Dietz Date: Tue Mar 17 09:42:32 2020 +0100 Dependency updates (#6138) * Jython RC * JavaFX commit eace7273f82dbd55ee6cc25f73723286183a5d7e Author: Oliver Kopp Date: Tue Mar 17 08:42:41 2020 +0100 Move "Manage field names & content" to "Edit" (#6137) commit 1b03f039aa4abf0812a3a8ae0ba248c08fdb39a3 Author: Oliver Kopp Date: Tue Mar 17 06:46:07 2020 +0100 Remove obsolete usage of File in DatabaseContext (#6135) commit f4fe97b93cb166da459d1301f4d11f0a99d6635c Author: eetian <57387212+eetian@users.noreply.github.com> Date: Mon Mar 16 22:27:36 2020 -0700 Fix warning display issue for entering a title made of two words (#6054) commit 6468f42fba6a6aaf0c3dd9212e8b1e9c44359e60 Merge: 2339e9585 57b583391 Author: Oliver Kopp Date: Tue Mar 17 06:23:57 2020 +0100 Merge pull request #6128 from JabRef/fix-citeseer-test Fix fetcher test: CiteSeerTest commit 2339e958599109cf92f4d69168e8fddff2007da0 Author: Oliver Kopp Date: Mon Mar 16 22:59:12 2020 +0100 Fix typo commit 98d0fa926029335e61ecfbb828332c70e30eef89 Author: Oliver Kopp Date: Mon Mar 16 22:58:12 2020 +0100 Fix typos commit fd2ac2499938c2253f0165fec8b83654fe486938 Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 16 20:36:19 2020 +0100 Bump postgresql from 42.2.10 to 42.2.11 (#6131) Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.10 to 42.2.11. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.10...REL42.2.11) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> commit 663815dd686f96d1936f08f318b2b088c965ebac Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 16 20:35:34 2020 +0100 Bump mockito-core from 3.3.1 to 3.3.3 (#6132) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.3.1 to 3.3.3. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.3.1...v3.3.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> commit deb8ae87893eefcac95fbde679b517db9d24b108 Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 16 20:35:19 2020 +0100 Bump org.beryx.jlink from 2.17.2 to 2.17.3 (#6133) Bumps org.beryx.jlink from 2.17.2 to 2.17.3. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> commit cbd26f11f54f4a46c2597b6ba564b76aa9d68c88 Author: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Mon Mar 16 09:36:07 2020 +0100 Small improvements in preferences dialog (#6130) * Changed special buttons to icon buttons * Changed ComboBox lists to use proper display name and added spinner to font size * Fixed KeyPress event * Reworded biblatex to BibLaTeX * l10n * Removed obsolete special pdf treatment in preferences * CHANGELOG.md and removed forgotten bindings * Fixed wrong biblatex fix * checkstyle commit 10f54688ae659c4ac3084769a985cce3c358169f Author: Oliver Kopp Date: Mon Mar 16 09:00:04 2020 +0100 Hotfix of tests commit c7e399c0210471ff96985524394663896ef23571 Merge: 0628f11e2 f52bd03a5 Author: Oliver Kopp Date: Mon Mar 16 07:45:59 2020 +0100 Merge pull request #6117 from JabRef/refactor-save Refactor SaveAction commit f52bd03a5e72ae7284767ee27f3174ba84ceaec6 Author: Oliver Kopp Date: Mon Mar 16 07:45:25 2020 +0100 Fix title refresh commit 57b583391a1823bcb50f2f8e8fc7d8c4fcda2be7 Author: Oliver Kopp Date: Sun Mar 15 21:52:10 2020 +0100 Fix fetcher test: CiteSeerTest commit 0628f11e26056f5d8f1703864e3989e9751b58a7 Merge: a71959f45 0f93e2244 Author: Oliver Kopp Date: Sun Mar 15 20:38:31 2020 +0100 Merge pull request #6126 from JabRef/google-captcha Minor fetcher improvements commit 3f9922e62e3a294bd19928227776f327b66158a3 Author: Stefan Kolb Date: Sun Mar 15 18:56:50 2020 +0100 More refactorings commit 0f93e2244d6500f1a6154c8433f2e2ad605b7939 Author: Stefan Kolb Date: Sun Mar 15 18:15:44 2020 +0100 Minor improvements for Google scholar fetcher Detect Google captcha div. Follow up PRs might show or give more info to the user. commit 8f68de937e53213bb3e778230c9b91a515f29ac9 Author: Stefan Kolb Date: Sun Mar 15 15:13:05 2020 +0100 Improve arXiv fetcher commit de790f92227efe01a80350638b78a5a3460e71ab Author: Stefan Kolb Date: Sun Mar 15 15:06:04 2020 +0100 Improve ACS fetcher commit a71959f45ebbec5543d866b68ebac9aecabc2391 Author: Stefan Kolb Date: Sun Mar 15 15:01:32 2020 +0100 Checkstyle commit 3eeb86ce4e6fbe457d589c080531af230290abff Author: Stefan Kolb Date: Sun Mar 15 15:00:17 2020 +0100 Improve SpringerLink fetcher commit 15c7981b23d46b06da9b7b468d42fc179e01d3ec Author: Stefan Kolb Date: Sun Mar 15 14:52:42 2020 +0100 Improve DoiResolution fetcher commit 086629d67ae49520bd4dc4ef5938ab03f0637ca7 Author: Stefan Kolb Date: Sun Mar 15 12:55:20 2020 +0100 Improve OpenAccessDoi fetcher commit 0784b4676f56ff387661f8e4efc1481830c28ad7 Merge: b5d5ed00e 01de672cc Author: github actions Date: Sun Mar 15 02:09:40 2020 +0000 Merge commit '01de672cc025432a6642f062a84e184d3a5408a1' commit 01de672cc025432a6642f062a84e184d3a5408a1 Author: github actions Date: Sun Mar 15 02:09:39 2020 +0000 Squashed 'src/main/resources/csl-styles/' changes from 6d7ca0a639..268df9ebc7 268df9ebc7 Create folia-biologica.csl (#4629) 2480646781 Update transversalites.csl (#4624) beae5fbeee Update instituto-de-pesquisas-energeticas-e-nucleares.csl (#4623) d7d70d2b61 Create australian-journal-of-agricultural-and-resource-economics.csl (#4626) cc2c6eb944 Update harvard-university-of-westminster.csl (#4622) 30767b007e Create marine-turtle-newsletter.csl (#4625) a2324aca6a Create journal-of-new-zealand-grasslands.csl (#4628) 1a87f4b474 Create china-national-standard-gb-t-7714-2015-author-date.csl (#4611) 2032fef017 Create china-national-standard-gb-t-7714-2015-note.csl (#4610) 3e00047af5 Create beilstein-open-access-journals.csl (#4613) b6c3363eae Update american-institute-of-aeronautics-and-astronautics.csl (#4614) cc68a6c4bb Remove et-al settings from american-chemical-society.csl (#4615) 2393106d8d Create rivista-trimestrale-di-sanita-pubblica-veterinaria.csl (#4617) 5f511cd6ce Update transversalites.csl (#4621) f052033527 Create aporia.csl (#4620) ae39d97603 Create the-american-journal-of-geriatric-psychiatry.csl (#4619) d61f56c13d Update centre-de-recherche-sur-les-civilisations-de-l-asie-orientale.csl (#4612) 96042851d7 Create united-nations-development-programme-icca-legal-review.csl (#4563) 79c92358a8 Create jiaptr.csl (#4609) a044ab2d1c Update groupe-danthropologie-et-darcheologie-funeraire.csl (#4599) beab323ac8 Create haemophilia.csl (#4601) git-subtree-dir: src/main/resources/csl-styles git-subtree-split: 268df9ebc775451896672d08fd4dcec9444f58f3 commit b5d5ed00e075e66fcbd34a1ac0cac37d1ee7d9ce Merge: ce7bb895e c29db2efe Author: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Sat Mar 14 23:10:22 2020 +0100 Merge pull request #6121 from JabRef/allow-icons-for-groups Icons from Material design icons not recognized commit ce7bb895e713931e533bd55d013e9c2bcec95ae8 Author: Siedlerchr Date: Sat Mar 14 20:23:18 2020 +0100 disable fail fast for the moment to let linux and windows build mac errors due to gradle dependecny fetching error commit 571a05163b1dbeb5e705d4f561e0f0d9b311b706 Author: Stefan Kolb Date: Sat Mar 14 20:14:53 2020 +0100 Fulltext file deleted when being renamed to the name differing just by letter case (#6123) Fixes #6120 commit c29db2efe6658942e30b8b2a17cfa68e871e3ab6 Author: Stefan Kolb Date: Sat Mar 14 14:27:19 2020 +0100 Icons from Material design icons not recognized Fixes #6078 Related #5245 commit 8dd744409158a343f2577d416fbbf7b6027b1371 Author: Siedlerchr Date: Sat Mar 14 12:14:25 2020 +0100 remove mavenLocal commit d9929d89f7c71f8599d5d1a6a7b918303dbcba74 Merge: 11d15cb8a 55171e667 Author: Siedlerchr Date: Sat Mar 14 12:05:30 2020 +0100 Merge remote-tracking branch 'upstream/master' * upstream/master: Update CHANGELOG.md Fix inconsistent capitalisation of downloaded files Improve arXiv fetcher (#6113) commit 11d15cb8a8ffc9db8b2e4c3e5d73572d2951aa55 Author: Siedlerchr Date: Sat Mar 14 12:04:51 2020 +0100 activate cache on mac limit has been increased to 5GB commit 55171e66749f6bd633b2d9a80724575a0607528b Merge: 209d336d2 a77585874 Author: Stefan Kolb Date: Sat Mar 14 12:02:31 2020 +0100 Merge pull request #6119 from JabRef/lowercase-file-extension Lowercase file extension commit a775858747bb9311696d3b35cd5d058d3d8b91dd Author: Stefan Kolb Date: Sat Mar 14 11:26:27 2020 +0100 Update CHANGELOG.md commit 45e2c84631e00d622e086e668409606544972667 Author: Stefan Kolb Date: Sat Mar 14 02:15:57 2020 +0100 Fix inconsistent capitalisation of downloaded files Fixes #6115 commit b3643969d8d8ed408e3df44102f8ce2c906ce2aa Author: Oliver Kopp Date: Sat Mar 14 01:15:05 2020 +0100 Refactor SaveAction - Get rid of "doSave()" - Add notification for non-successful save in case of "SaveAll" - Remove deprecated "setDatabaseFile" (as we are Java8 with Path) Co-authored-by: Stefan Kolb commit 209d336d24eef2a4bf4405d6d334370466d88a65 Author: Tobias Diez Date: Fri Mar 13 23:22:31 2020 +0100 Improve arXiv fetcher (#6113) No longer include the version string in the `eprint` field, as wished in https://discourse.jabref.org/t/remove-version-in-arxiv-import/1941. Also improved the arXiv identifier parser a bit. Co-authored-by: Christoph commit aab7a08e7f22fdc306767e775c8be4d1d8866544 Author: Oliver Kopp Date: Fri Mar 13 22:45:39 2020 +0100 Add CHANGELOG.md for https://github.com/JabRef/jabref/pull/6112 commit d954880425dad0dc9527ff7ed573eddcf915d522 Author: Oliver Kopp Date: Fri Mar 13 22:32:42 2020 +0100 Try to disable code comments Source: https://docs.codecov.io/docs/pull-request-comments#section-configuration commit 8b7c374530786bf9e32a1d21dc7cb8bb494c2cd2 Author: Stefan Kolb Date: Fri Mar 13 22:17:31 2020 +0100 fix: exception when opening already opened files (#6114) commit 3c0c518078da5650ecba051d23d602c973357c22 Merge: ae30bf002 45200d39a Author: Stefan Kolb Date: Fri Mar 13 22:17:24 2020 +0100 Merge pull request #6112 from JabRef/order-group-search ux: move group filter bar to top commit 45200d39ad24b766a4a6fb9905d011bb2188b377 Author: Stefan Kolb Date: Fri Mar 13 22:02:34 2020 +0100 Increase size of expand groups icon commit fe76a945f437157ec7d0b08258655f7638f1c201 Author: Stefan Kolb Date: Fri Mar 13 20:54:54 2020 +0100 ux: move group filter bar to top Switch search field and new group button Cleanups Co-authored-by: Oliver Kopp commit ae30bf002fc29f7a006edc74c643063c6b0c22bc Author: Oliver Kopp Date: Fri Mar 13 19:44:59 2020 +0100 Structure SUMMARY.md and add local build to jpackage.md commit b02f982d409c78af4e9feb18b55eece44defe30e Author: Stefan Kolb Date: Fri Mar 13 19:31:05 2020 +0100 Really create new lookup menu (#6111) Refs https://github.com/JabRef/jabref/pull/5989 Fixes https://github.com/koppor/jabref/issues/404 commit 23034ce7b76230fa82ef0b2d1bb91b053083aeeb Author: Tobias Diez Date: Thu Mar 12 14:49:52 2020 +0100 Delete CustomJFXPanel.java (#6106) commit 49e4d41a1d3c7982eb90d429a482891c40237226 Author: Tobias Diez Date: Thu Mar 12 14:37:04 2020 +0100 Remove gui globals (#6103) * Remove GUIGlobals * Deprecate Globals * Fix checkstyle commit 43f533bcf6e7729925e21b58f30c48becc584f30 Author: Oliver Kopp Date: Tue Mar 10 11:03:51 2020 +0100 Update gradle and git checkout (#6090) * Update "actions/checkout" from v1 to v2 --> See https://github.com/actions/checkout * Update gradle from 6.2.1 to 6.2.2 --> See https://docs.gradle.org/6.2.2/release-notes.html commit d5ee20bbbfcf119185505e465cb587d00ae644c4 Author: Oliver Kopp Date: Tue Mar 10 09:06:48 2020 +0100 Disable codecov comments Documentation: https://docs.codecov.io/docs/pull-request-comments#section-disable-comment commit 52f35717465a1d65763f8b958e2d6865d0295b6d Author: Oliver Kopp Date: Tue Mar 10 08:28:20 2020 +0100 Add our statement on code quality commit 60958adbf9027ad665ec90b352b0344e4edd66d5 Author: Oliver Kopp Date: Tue Mar 10 08:26:48 2020 +0100 Add link to code quality. commit a299f84e2a30a6b25fd9c81aecb6184880223337 Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 9 19:52:30 2020 +0100 Bump xmlunit-core from 2.6.3 to 2.6.4 (#6083) Bumps [xmlunit-core](https://github.com/xmlunit/xmlunit) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/master/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.6.3...v2.6.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> commit 70d4dcf770b6344ebaa45aaef6a9e0b766fc4425 Author: Christoph Date: Mon Mar 9 19:51:48 2020 +0100 New translations JabRef_en.properties (Turkish) (#6079) commit deef43084ba0b11da812ef248251b1d7c13a352c Author: Siedlerchr Date: Mon Mar 9 19:49:06 2020 +0100 Try again with off in codecov +semver: minor commit 0e2b1a4e79ccedfea4eb0b067aa3a9f0ee7980bb Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 9 19:35:04 2020 +0100 Bump xmlunit-matchers from 2.6.3 to 2.6.4 (#6082) Bumps [xmlunit-matchers](https://github.com/xmlunit/xmlunit) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/master/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.6.3...v2.6.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> commit 6bff9366c39a4e80383b9b54b984ccb6ecb5cfbb Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 9 17:31:54 2020 +0100 Bump unirest-java from 3.6.00 to 3.6.01 (#6084) Bumps [unirest-java](https://github.com/Kong/unirest-java) from 3.6.00 to 3.6.01. - [Release notes](https://github.com/Kong/unirest-java/releases) - [Changelog](https://github.com/Kong/unirest-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/Kong/unirest-java/compare/v3.6.00...v3.6.01) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> commit 5fc2052d469777c37f7e753e4933748b36cfbfee Author: Oliver Kopp Date: Sun Mar 8 23:24:21 2020 +0100 Add some grouping on example library commit 93de1384f9dab2c46f7c081b4e55912c02a6e759 Author: Oliver Kopp Date: Sun Mar 8 22:55:23 2020 +0100 Farewell Jörg - maybe you come back at some time commit 37af676bc1d8d2d04f8a5783f446fa78d9aaa010 Author: Oliver Kopp Date: Sun Mar 8 22:29:10 2020 +0100 Fix exception when creating a new library (#6080) commit c8740800771e9c61a7fbc9f8ff8b118b1d393184 Author: Christoph Date: Sat Mar 7 23:14:46 2020 +0100 New Crowdin translations (#6075) * New translations JabRef_en.properties (Chinese Simplified) * New translations JabRef_en.properties (Dutch) * New translations JabRef_en.properties (French) * New translations JabRef_en.properties (German) * New translations JabRef_en.properties (Greek) * New translations JabRef_en.properties (Danish) * New translations JabRef_en.properties (Portuguese, Brazilian) * New translations JabRef_en.properties (Turkish) * New translations JabRef_en.properties (Tagalog) * New translations JabRef_en.properties (Swedish) * New translations JabRef_en.properties (Spanish) * New translations JabRef_en.properties (Russian) * New translations JabRef_en.properties (Persian) * New translations JabRef_en.properties (Portuguese) * New translations JabRef_en.properties (Polish) * New translations JabRef_en.properties (Norwegian) * New translations JabRef_en.properties (Japanese) * New translations JabRef_en.properties (Italian) * New translations JabRef_en.properties (Indonesian) * New translations JabRef_en.properties (Vietnamese) commit 1a36304b5ed8f76594c89e4db91c46a267a73efa Author: Oliver Kopp Date: Fri Mar 6 23:21:45 2020 +0100 Remove duplicated CHANGELOG.md line commit 3d24714deb07978972b921025551d5328faff039 Author: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Fri Mar 6 15:31:23 2020 +0100 [WIP] Remove BaseAction from JabRefFrame (#6056) * Added UndoRedoAction * Refactored AppendDatabaseAction * Refactored ReplaceStringAction and GenerateBibtexKeyAction * Refactored CleanupAction * Added OpenEntryEditorAction, fixed bug about selected entries without open database * Refactored AbbreviateAction, merged UnabbreviateAction, removed unused Actions * Refactored DownloadFullTextAction * Added PullChangesFromSharedAction * Added SaveAction, removed remaining deprecated OldDatabaseCommandWrapper, BaseAction and Actions * Added UndoRedoAction * Refactored AppendDatabaseAction * Refactored ReplaceStringAction and GenerateBibtexKeyAction * Refactored CleanupAction * Added OpenEntryEditorAction, fixed bug about selected entries without open database * Refactored AbbreviateAction, merged UnabbreviateAction, removed unused Actions * Refactored DownloadFullTextAction * Added PullChangesFromSharedAction * Added SaveAction, removed remaining deprecated OldDatabaseCommandWrapper, BaseAction and Actions * Removed test, added default case for codacy compliance * Removed test for notifications commit 8060338198003462a3914f23674ce08a76fde9d3 Author: Christoph Date: Fri Mar 6 14:35:25 2020 +0100 New translations JabRef_en.properties (French) (#6071) commit 55234f649cf3b669b52422e5cfc2e127cbeb3b8d Author: JabRef Date: Fri Mar 6 12:22:16 2020 +0000 GitBook: [master] 23 pages modified commit b6d6b797abee75131d6aebf37d616dbcd72d4b73 Merge: d9a808800 17eb38bd1 Author: Oliver Kopp Date: Fri Mar 6 11:18:20 2020 +0100 Merge branch 'replace_deprecated' commit d9a80880080cc906a43ff666db92e438496f4f14 Author: Oliver Kopp Date: Fri Mar 6 10:58:32 2020 +0100 Add month normalization to AstroPhysicsFetcher (#6022) commit 9c0fa85c4ab1653f1654427db2a41c0d166ce20a Author: Oliver Kopp Date: Fri Mar 6 10:57:43 2020 +0100 Code: Tex --> Latex (in the context of ParseLatex) (#6019) commit 548d33c84582a4cf83b98147862d8e721895b9e2 Author: Stefan Kolb Date: Fri Mar 6 10:57:22 2020 +0100 Shutdown should be fixed since v2.1.2 (#5993) commit 17eb38bd1b46b6b631fb7f559157d3a2cdca872d Author: Oliver Kopp Date: Fri Mar 6 10:50:50 2020 +0100 Fix CHANGELOG.md order commit 11545e1dc1159ca5d1081aa015f16dcb33798693 Merge: 7fda4cc79 ac295cb30 Author: Oliver Kopp Date: Fri Mar 6 10:50:15 2020 +0100 Merge branch 'master' into replace_deprecated commit 7fda4cc791eb43d5f5f15371829724bae9d813c9 Author: David Stevens Date: Fri Mar 6 10:49:15 2020 +0100 Add ShortScience integration (#6018) commit ac295cb30aca6448281b2710b3e7af917f8bdce1 Author: Oliver Kopp Date: Fri Mar 6 10:33:38 2020 +0100 Shorten CHANGELOG.md intro text commit 3839110e2b63146e85484622c53ac0365f010eaf Author: Oliver Kopp Date: Fri Mar 6 10:32:51 2020 +0100 Show development information commit 2e6f4331db87f5e0fc584bb15c2a8887e669e0c8 Author: Oliver Kopp Date: Fri Mar 6 06:57:35 2020 +0100 Trigger build commit 60ff83a5f60d97d764bd76fd9762cd76eefdc983 Author: Oliver Kopp Date: Fri Mar 6 09:43:06 2020 +0100 Fix -ci suffix removal commit 289142fc9772a0f732c65b27aa22253ef4460706 Author: Oliver Kopp Date: Fri Mar 6 09:02:47 2020 +0100 Trigger build commit a3d67cca0db54c3bed4b20fd10647f9f34e10948 Author: Christoph Date: Fri Mar 6 09:02:00 2020 +0100 Fix exception when adding save action without selected formatter (#6072) commit b28626b0a31006c25574f641fb4cb2b6c194d1cd Author: github actions Date: Fri Mar 6 05:58:21 2020 +0000 Squashed 'src/main/resources/csl-styles/' changes from 265a3a9061..6d7ca0a639 6d7ca0a639 Update associacao-brasileira-de-normas-tecnicas-ufrgs.csl (#4598) 6049caa776 Update geistes-und-kulturwissenschaften-heilmann.csl (#4597) 9526038b9b journal title in italics in biophysical-journal.csl (#4595) 9ab51f94f8 Create the-university-of-tokyo-law-review.csl (#4593) 126f84c986 Always show DOI for article-journal for journal-of-forensic-sciences.csl (#4596) git-subtree-dir: src/main/resources/csl-styles git-subtree-split: 6d7ca0a639709a9e4148011acd599b2922d3bf1d commit fdd87976d5b9e5820f3bdd87852f43760be0b761 Merge: b81d24846 b28626b0a Author: github actions Date: Fri Mar 6 05:58:21 2020 +0000 Merge commit 'b28626b0a31006c25574f641fb4cb2b6c194d1cd' commit b81d248465094ca68f8a5da188f5b9c024289cb9 Author: Oliver Kopp Date: Fri Mar 6 06:57:35 2020 +0100 Trigger build commit 35c3c95237f1c220dba9893653b7303d20e4fe10 Author: Oliver Kopp Date: Fri Mar 6 06:13:38 2020 +0100 Fix release date commit 20e76a32547a3f5f1642371d9713dce5093f675a Author: Oliver Kopp Date: Fri Mar 6 06:51:49 2020 +0100 Explicitly list more external libraries commit b6762284338bee65e451055e4a1bec0077c47eac Author: Oliver Kopp Date: Thu Mar 5 06:37:40 2020 +0100 Ignore extracted JDK 14 commit 22173da712eccc78a8ba3d24c638aeaf12168a36 Author: Oliver Kopp Date: Fri Mar 6 05:46:00 2020 +0100 Revert "Update JDK14 from 34 to Build 36 (2020/2/6) - Release Candidate" This reverts commit fd08f8d7248682256d517d81fdf7fc246ca85d49. commit 3ac6331c9f99b2daa32a40ca03a64512c9f8e7d6 Author: Tobias Diez Date: Thu Mar 5 11:00:26 2020 +0100 Disable codecov comments commit 84b1d5bba2f944fa67e1221f05902e0b5e3a8ca2 Author: Tobias Diez Date: Thu Mar 5 10:51:05 2020 +0100 Updated screenshot and added hint on the FAQ (#6058) commit 819df76e1a62b9b24ebe3610dc7976b8f59a1b01 Author: Tobias Diez Date: Thu Mar 5 09:57:17 2020 +0100 Readd package portable commit a2093998d54a7292a58a80221ebf900d79906797 Author: Tobias Diez Date: Thu Mar 5 09:51:26 2020 +0100 Add debug output commit d8ef1735303ccab5b05e92e56ac855ff99b8f1d8 Merge: d27867f73 4bdedcb2f Author: Oliver Kopp Date: Thu Mar 5 06:09:33 2020 +0100 Merge branch 'master' of github.com:JabRef/jabref commit d27867f7352b95ad613dad7397f6e446f8398477 Author: Oliver Kopp Date: Thu Mar 5 06:08:01 2020 +0100 Move snap publishing to own workflow commit 4bdedcb2fe96ee24b23184195cd79bcfe4f610ad Merge: 6a4fa61f0 dbd48659b Author: github actions Date: Thu Mar 5 05:00:53 2020 +0000 Merge commit 'dbd48659bba98c38d48b902641cfda93aa211a61' commit dbd48659bba98c38d48b902641cfda93aa211a61 Author: github actions Date: Thu Mar 5 05:00:53 2020 +0000 Squashed 'src/main/resources/csl-locales/' changes from 2b94bf9fac..d0ee4d13c9 d0ee4d13c9 Update CONTRIBUTING.md git-subtree-dir: src/main/resources/csl-locales git-subtree-split: d0ee4d13c942648c2c88629aeb01f03a17fac72c commit cd707467ab5ffb7d5c0097661af54551027d7097 Author: Oliver Kopp Date: Wed Mar 4 22:52:48 2020 +0100 Remove obsolete step commit 1d6887e371cb53e00686aec5f743c117e3c9aadb Author: Oliver Kopp Date: Wed Mar 4 22:35:33 2020 +0100 Add debug output for snap file location commit ecf822e1dee8a18dd248a3f1c99220a9ed665417 Author: Oliver Kopp Date: Wed Mar 4 22:20:31 2020 +0100 Try to build snapcraft image also on master-release commit 29ed8fe0f9ad2c4a8dff5c19fb45e103c89cf24f Merge: d8e4c1187 1a7a4df51 Author: Oliver Kopp Date: Wed Mar 4 22:16:35 2020 +0100 Merge remote-tracking branch 'origin/snapcraft-publish-action' into master-release commit 1a7a4df515ee53d39d2c12921d214de67a0c3f6a Author: Oliver Kopp Date: Wed Mar 4 22:08:50 2020 +0100 Build snap only for master branch commit d8e4c11872619ab4e5dc1375e9a2cef57b5f1700 Author: Oliver Kopp Date: Wed Mar 4 17:40:22 2020 +0100 Update GitVersion action from v0.3 to v0.9.1 (#6061) commit 6a4fa61f0e15a5b5498f50b4b1633c45fe4237fc Author: Oliver Kopp Date: Wed Mar 4 17:44:23 2020 +0100 Add empty line to deployment.yml to trigger build commit 2a1367438e015ce1094fca812295d7dc800c9713 Author: Oliver Kopp Date: Tue Mar 3 21:41:03 2020 +0100 Release v5.0 commit 6a4df6f09b915f8f66ccb05d52f2bf3b317eac31 Author: Oliver Kopp Date: Wed Mar 4 21:07:57 2020 +0100 Refine external-libraries.txt commit 3d9eccce7e757ac56a4db15cd5cf0a5fa1da69e2 Author: Oliver Kopp Date: Wed Mar 4 20:53:48 2020 +0100 Revert "Remove ci prefix for releases" This reverts commit 8660e290a9e36103dcc30b81cedc37815c17eeaa. commit 2978deea1f713055921ee047cd9d0657db204627 Author: Oliver Kopp Date: Wed Mar 4 20:53:12 2020 +0100 Remove suffix "-ci.1" from version information stored in BuildInfo commit 8660e290a9e36103dcc30b81cedc37815c17eeaa Author: Tobias Diez Date: Wed Mar 4 20:06:15 2020 +0100 Remove ci prefix for releases commit 8fdfcfa7760ed56c00fc561663443dfa34003cd9 Author: Galileo Sartor Date: Wed Mar 4 16:40:10 2020 +0100 Use github action to publish snap commit 21c6e5e680c1d0ff4d0e278c2dc89a4f86bbb68d Author: Galileo Sartor Date: Wed Mar 4 15:19:56 2020 +0100 snap: fix for native-messaging-host permissions (#6062) * snap: fix for native-messaging-host permissions add hook for chrome/chromium * snap: fix no newline * Add snap browser integration to changelog commit 8111707f7edac23bbe54f3abc7bc0acb1ba37809 Author: Oliver Kopp Date: Wed Mar 4 01:13:45 2020 +0100 Refine list of external libraries commit fd08f8d7248682256d517d81fdf7fc246ca85d49 Author: Oliver Kopp Date: Tue Mar 3 23:05:00 2020 +0100 Update JDK14 from 34 to Build 36 (2020/2/6) - Release Candidate Source: https://jdk.java.net/14/ commit 3f9a508a80c2145aecdca9bc43346216f57d60d5 Author: Oliver Kopp Date: Tue Mar 3 21:46:57 2020 +0100 Also release a binary in the "master-release" branch (which is to test releases) commit e7d3f0bec312eb0e4d8b2e2b8a5b5a1dc616415b Author: Oliver Kopp Date: Tue Mar 3 21:27:23 2020 +0100 Refine CHANGELOG.md (markdown-lint) commit ffef04ab0e2b215988ca89d46df27c821cb8aabb Author: Oliver Kopp Date: Tue Mar 3 20:39:24 2020 +0100 Add authors commit 7f3c556e01212917bd434157cb00f8d9166bd61b Author: Oliver Kopp Date: Tue Mar 3 20:41:30 2020 +0100 Add authors of 5fa1dcfe0430c933eea1257e4b0a3705f41304ec commit eb521ad27ef110b8fa694dfe6fd815bc474c5021 Author: Christoph Date: Tue Mar 3 23:10:38 2020 +0100 New Crowdin translations (#6059) commit d88df2b5cfc1b1bb0d2a735a190e28338e5956dd Author: Oliver Kopp Date: Tue Mar 3 23:00:58 2020 +0100 Lint README.md commit 01fdf599458f5854b91c9c40fd96dc4cfd2de6ab Merge: adce9b923 ed0c6d861 Author: github actions Date: Tue Mar 3 20:42:27 2020 +0000 Merge commit 'ed0c6d8616adcd46491588910d63d86ef1f3dfdc' commit ed0c6d8616adcd46491588910d63d86ef1f3dfdc Author: github actions Date: Tue Mar 3 20:42:26 2020 +0000 Squashed 'src/main/resources/csl-styles/' changes from 1ab0241bbf..265a3a9061 265a3a9061 Update john-benjamins-publishing-company-linguistik-aktuell-linguistics-today.csl (#4588) bfcd47c20e Update journal-of-forensic-sciences.csl (#4590) 165be9c670 Create laboratory-animal-science-professional.csl (#4591) b51b8e63e1 Create pediatric-infectious-disease-journal.csl (#4589) git-subtree-dir: src/main/resources/csl-styles git-subtree-split: 265a3a9061c827d267b8289bb7390efca5b89d01 commit adce9b92374964805e7a01f5870c5ec2b747fd90 Author: Jonas Lähnemann Date: Tue Mar 3 21:16:01 2020 +0100 JournalList: Minor corrections (#6043) * Corrections to Physics Status Solidi Wrong abbreviations were coming last and thus took precedence over the correct ones in the unabbreviator. commit a03bad1ac7bdae078bf49a072cc33f74e444e986 Author: github actions Date: Tue Mar 3 14:42:15 2020 +0000 Squashed 'src/main/resources/csl-styles/' changes from 566df871e2..1ab0241bbf 1ab0241bbf bibtex.csl - remove creator lables 722ba77e02 AGLC: Remove short-title from bibliography git-subtree-dir: src/main/resources/csl-styles git-subtree-split: 1ab0241bbf4dc8f3ae8fc7bd5c5b517c9b29cd15 commit 6731c431f5c63a55127d06d326adc4d1631cf65d Merge: f9088833c a03bad1ac Author: github actions Date: Tue Mar 3 14:42:15 2020 +0000 Merge commit 'a03bad1ac7bdae078bf49a072cc33f74e444e986' commit c0abdce95a0fadacae1b69331b3b2512f0fc0f81 Merge: 789799b15 f9088833c Author: Carl Christian Snethlage Date: Tue Mar 3 15:40:40 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated commit f9088833c084aee4081d076e9a049dab977cd4a7 Author: Tobias Diez Date: Mon Mar 2 22:20:39 2020 +0100 Add use statement for mariadb. Fixes #5886. commit e6e2da34cd3927c230f87530dea5d1dab6874026 Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 2 13:18:55 2020 +0100 Bump jsoup from 1.12.2 to 1.13.1 (#6052) Bumps [jsoup](https://github.com/jhy/jsoup) from 1.12.2 to 1.13.1. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES) - [Commits](https://github.com/jhy/jsoup/compare/1.12.2...jsoup-1.13.1) Signed-off-by: dependabot-preview[bot] commit d48182dcfe4d416baf267bf1a99c0178fc1a1e0f Author: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon Mar 2 13:18:15 2020 +0100 Bump checkstyle from 8.29 to 8.30 (#6051) Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 8.29 to 8.30. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.29...checkstyle-8.30) Signed-off-by: dependabot-preview[bot] commit 01289321c9e62d212ace38dbc42ca97eb72872c3 Author: Oliver Kopp Date: Sun Mar 1 22:06:53 2020 +0100 Update year commit 0d14227f5d4e0b8b5ffed173a82cdc9af5aaed57 Author: Christoph Date: Sun Mar 1 21:34:43 2020 +0100 Fix fetcher tests using correct month (#6050) commit 7804dbba04815a0890613beb32cb88a6e6912e39 Author: Christoph Date: Sun Mar 1 21:17:00 2020 +0100 New Crowdin translations (#6047) commit ed5309bc4dcdd904b82876f4bcc5e912b94770e5 Author: github actions Date: Sun Mar 1 02:09:45 2020 +0000 Squashed 'src/main/resources/csl-locales/' changes from d73af4a3fe..2b94bf9fac 2b94bf9fac Fix markdown lint error "[no-literal-urls] Don’t use literal URLs without angle brackets" (#203) git-subtree-dir: src/main/resources/csl-locales git-subtree-split: 2b94bf9fac34a2f6b73f4d320144e5c0cb8a05bc commit 4902c40f9ee4fceb1bf90e0f4985a686da3514c6 Merge: 8e26bd8ad ed5309bc4 Author: github actions Date: Sun Mar 1 02:09:45 2020 +0000 Merge commit 'ed5309bc4dcdd904b82876f4bcc5e912b94770e5' commit 6d8efb6e8915b1fe16a9fa9c36b7953370395b44 Author: github actions Date: Sun Mar 1 02:09:42 2020 +0000 Squashed 'src/main/resources/csl-styles/' changes from f03decef0e..566df871e2 566df871e2 Create archeologicke-rozhledy.csl (#4586) 420558620b Update mcgill-en.csl - include render-legislation macro (#4579) 7d857f1df5 Create journal-of-plankton-research.csl (#4581) 05c8aca5bd Update mercator-institut-fur-sprachforderung-und-deutsch-als-zweitsprache.csl (#4582) 564dc5f633 Update vodohospodarske-technicko-ekonomicke-informace.csl (#4584) git-subtree-dir: src/main/resources/csl-styles git-subtree-split: 566df871e2daa3c8903f8a31c92ccff338c21d14 commit 8e26bd8ad1ba6a31aa207f9373aba6126307501a Merge: be9c788de 6d8efb6e8 Author: github actions Date: Sun Mar 1 02:09:42 2020 +0000 Merge commit '6d8efb6e8915b1fe16a9fa9c36b7953370395b44' commit be9c788de804c2bd9e3abaf76b082b6b2e82e66f Author: Oliver Kopp Date: Sat Feb 29 09:39:25 2020 +0100 Rephrase IntelliJ paragraph commit 13c1ff6f0fccf89deb2b3fc2e99239c795bea0af Author: Oliver Kopp Date: Sat Feb 29 09:35:30 2020 +0100 Update gradle from 6.2 to 6.2.1 (#6048) commit 789799b1554eca9d125d4de6e41f16731e11eb69 Author: Carl Christian Snethlage Date: Fri Feb 28 19:16:52 2020 +0100 Fixed merge error commit e4136b54a27f9e2c541509248265eaaf36cf6b04 Merge: 88ca460b9 1ab300bf2 Author: Carl Christian Snethlage Date: Fri Feb 28 18:26:51 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated commit 88ca460b9d1e5e10bc50d3fb9767fd74ea73e8ec Merge: 3c7da13f4 9424777b9 Author: Carl Christian Snethlage Date: Fri Feb 28 11:48:05 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated # Conflicts: # src/main/java/org/jabref/gui/JabRefFrame.java commit 3c7da13f4968f3e4f7f21ad94aec7bcc252080b4 Author: Carl Christian Snethlage Date: Thu Feb 27 13:01:49 2020 +0100 Fixed import order commit 2719c9260ddd4f107c6699a6d0ec9a2ebe6b5c09 Author: Carl Christian Snethlage Date: Thu Feb 27 10:29:38 2020 +0100 CodeCov and moved EditAction to edit package commit da1b68936a6d2e0960242aa53a12007770d6c53b Merge: 16409e322 1d4efaa2f Author: Carl Christian Snethlage Date: Thu Feb 27 08:53:46 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated commit 16409e3226d3fcae3dc820518794418286371740 Author: Carl Christian Snethlage Date: Sun Feb 23 17:41:28 2020 +0100 l10n commit dcdef689f0ba013898e2fddb498ed0c169f8c1ca Author: Carl Christian Snethlage Date: Sun Feb 23 17:22:12 2020 +0100 Extracted BaseAction out of EditAction commit a800bf4cde1f1fac6b45c1dae0063e96d114fc92 Author: Carl Christian Snethlage Date: Sat Feb 22 19:42:47 2020 +0100 Fixed possible NPE in SpecialFieldAction commit a5bf6b7af98596354410b5fb3f0da047479bb32c Author: Carl Christian Snethlage Date: Sat Feb 22 19:27:30 2020 +0100 Extracted BaseAction out of SpecialFieldMenuItemFactory commit c84be953371847ffb37f8771b287fa25984d1d2e Merge: 9b8e385eb d38f81348 Author: Carl Christian Snethlage Date: Sat Feb 22 15:29:48 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated # Conflicts: # src/main/java/org/jabref/gui/JabRefFrame.java commit 9b8e385eb4cc13ae25116287b0750e621dba6683 Author: Carl Christian Snethlage Date: Thu Feb 20 23:40:10 2020 +0100 CHANGELOG.md commit 08fa945653e36bc705513fec9ced36e35410c3bd Author: Carl Christian Snethlage Date: Thu Feb 20 14:18:37 2020 +0100 Fixed l10n merge errors commit 1dc67db379051ee2908e19de797637fb0802d6a8 Merge: bca3a0b9f 5fa1dcfe0 Author: Carl Christian Snethlage Date: Thu Feb 20 14:09:29 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated # Conflicts: # src/main/resources/l10n/JabRef_en.properties commit bca3a0b9f0767c5dcf79adb8ad57f63c50233372 Author: Carl Christian Snethlage Date: Thu Feb 20 14:08:32 2020 +0100 Refactored ActionHelper, removed some deprecated Actions and minor corrections commit f922be3b638ac4e5c8da8ee3a5903beaa307c00a Merge: def0e67d4 c768697e4 Author: Carl Christian Snethlage Date: Wed Feb 19 17:20:20 2020 +0100 Merge remote-tracking branch 'upstream/master' into replace_deprecated # Conflicts: # src/main/resources/l10n/JabRef_en.properties commit def0e67d43a7d604479bf17ba125f0dae43e673e Author: Carl Christian Snethlage Date: Wed Feb 19 17:11:58 2020 +0100 l10n and some minor corrections commit b83d547af4ac0db3a4df350301c2a45487f81054 Author: Carl Christian Snethlage Date: Wed Feb 19 00:34:41 2020 +0100 Refactored CopyBibTeXKeyAndLinkAction, copyTitle, copyCiteKey, copyKey, copyKeyAndTitle to CopyMoreActions commit e1a6e78eaed85d953ea191cefd261dbcdf12bfce Author: Carl Christian Snethlage Date: Tue Feb 18 21:41:58 2020 +0100 Refactored CitationStyleToClipboardWorkerTest to CopyCitationAction commit 18a3439bad26f09acc39b0594776e1ae6c54bb57 Author: Carl Christian Snethlage Date: Tue Feb 18 20:41:42 2020 +0100 Refactored AttachFileAction commit b71360bd16ad307ac15b86bee828d0338eee49fd Author: Carl Christian Snethlage Date: Tue Feb 18 20:32:52 2020 +0100 Refactored SendAsEMailAction commit f664d10470572e00e1f5c91bbc3eec83853abb92 Author: Carl Christian Snethlage Date: Tue Feb 18 18:41:18 2020 +0100 Added OpenUrlAction, Refactored MergeEntriesAction, MergeWitchFetchedEntryAction, AttachFileAction commit d1d74c7d4d12c81b1b324fc4f973f2f437b2bb83 Author: Carl Christian Snethlage Date: Mon Feb 17 23:14:23 2020 +0100 OpenConsole, OpenFolder, OpenExternalFile commit 0d2efc46c9a595786f46ce170e1bdd239b7827d8 Author: Carl Christian Snethlage Date: Mon Feb 17 20:45:10 2020 +0100 Preview and XMPWriter --- .github/workflows/deployment.yml | 96 ++-- .github/workflows/snap.yml | 26 + .github/workflows/tests-fetchers.yml | 3 + .github/workflows/tests-oracle.yml | 3 + .github/workflows/tests.yml | 28 +- .gitignore | 3 + .mailmap | 2 + AUTHORS | 13 + CHANGELOG.md | 86 +-- DEVELOPERS | 1 - GitVersion.yml | 4 +- LICENSE.md | 2 +- README.md | 23 +- build.gradle | 23 +- codecov.yml | 4 +- docs/README.md | 40 +- docs/SUMMARY.md | 18 + ...markdown-architectural-decision-records.md | 9 +- docs/adr/0001-use-crowdin-for-translations.md | 1 + docs/adr/0002-use-slf4j-for-logging.md | 7 +- docs/adr/0003-use-gradle-as-build-tool.md | 3 +- docs/adr/0004-use-mariadb-connector.md | 12 +- ...fully-support-utf8-only-for-latex-files.md | 14 +- ...nly-translated-strings-in-language-file.md | 13 +- docs/adr/0007-human-readable-changelog.md | 13 +- docs/adr/README.md | 5 +- docs/adr/template.md | 83 +-- docs/code-howtos.md | 180 +++--- docs/code-quality.md | 16 +- docs/custom-svg-icons.md | 9 +- docs/debugging-jpackage-installations.md | 17 - docs/development-strategy.md | 42 +- ...elines-for-setting-up-a-local-workspace.md | 209 ++++--- docs/high-level-documentation.md | 12 +- docs/javafx.md | 73 +-- docs/jpackage.md | 39 ++ docs/testing.md | 6 +- docs/tools.md | 48 +- docs/ui-recommendations.md | 11 +- external-libraries.txt | 280 ++++++++-- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 3 + scripts/generate-authors.sh | 4 +- ...-plug-etc-chromium-native-messaging-jabref | 9 + ...lug-etc-opt-chrome-native-messaging-jabref | 9 + ...ug-hostfs-mozilla-native-messaging-jabref} | 2 +- ...-plug-etc-chromium-native-messaging-jabref | 7 + ...lug-etc-opt-chrome-native-messaging-jabref | 7 + ...ug-hostfs-mozilla-native-messaging-jabref} | 2 +- snap/snapcraft.yaml | 49 +- src/main/java/module-info.java | 1 - src/main/java/org/jabref/Globals.java | 12 +- src/main/java/org/jabref/JabRefGUI.java | 5 +- src/main/java/org/jabref/JabRefMain.java | 8 +- .../org/jabref/cli/ArgumentProcessor.java | 2 +- src/main/java/org/jabref/cli/JabRefCLI.java | 2 +- src/main/java/org/jabref/gui/BasePanel.java | 527 +----------------- src/main/java/org/jabref/gui/GUIGlobals.java | 55 -- src/main/java/org/jabref/gui/JabRefFrame.java | 295 ++++------ .../org/jabref/gui/OpenConsoleAction.java | 34 ++ .../gui/{worker => }/SendAsEMailAction.java | 56 +- .../java/org/jabref/gui/StateManager.java | 13 + .../org/jabref/gui/actions/ActionHelper.java | 23 + .../java/org/jabref/gui/actions/Actions.java | 72 --- .../org/jabref/gui/actions/BaseAction.java | 15 - .../org/jabref/gui/actions/JabRefAction.java | 6 +- .../jabref/gui/actions/OldCommandWrapper.java | 56 -- .../OldCommandWrapperForActiveDatabase.java | 49 -- .../actions/OldDatabaseCommandWrapper.java | 63 --- .../jabref/gui/actions/StandardActions.java | 3 +- .../GenerateBibtexKeyAction.java | 70 +-- .../org/jabref/gui/cleanup/CleanupAction.java | 116 ++-- .../cleanup/FieldFormatterCleanupsPanel.java | 2 + .../jabref/gui/customjfx/CustomJFXPanel.java | 21 - .../jabref/gui/desktop/os/DefaultDesktop.java | 6 - .../java/org/jabref/gui/desktop/os/Linux.java | 23 - .../jabref/gui/desktop/os/NativeDesktop.java | 9 - .../java/org/jabref/gui/desktop/os/OSX.java | 6 - .../org/jabref/gui/desktop/os/Windows.java | 22 - ...eUIManager.java => AutosaveUiManager.java} | 11 +- .../gui/edit/CopyBibTeXKeyAndLinkAction.java | 65 --- .../org/jabref/gui/edit/CopyMoreAction.java | 238 ++++++++ .../java/org/jabref/gui/edit/EditAction.java | 76 +++ .../jabref/gui/edit/ReplaceStringAction.java | 14 +- .../jabref/gui/entryeditor/EntryEditor.java | 9 - .../LatexCitationsTabViewModel.java | 12 +- .../entryeditor/OpenEntryEditorAction.java | 25 + .../gui/entryeditor/PreviewSwitchAction.java | 31 ++ .../gui/entryeditor/RelatedArticlesTab.java | 2 +- .../errorconsole/ErrorConsoleViewModel.java | 2 +- .../org/jabref/gui/exporter/SaveAction.java | 51 ++ .../jabref/gui/exporter/SaveAllAction.java | 12 +- .../gui/exporter/SaveDatabaseAction.java | 291 +++++----- .../jabref/gui/exporter/WriteXMPAction.java | 33 +- .../externalfiles/DownloadFullTextAction.java | 73 ++- .../gui/fieldeditors/LinkedFileViewModel.java | 30 +- .../jabref/gui/filelist/AttachFileAction.java | 33 +- .../jabref/gui/groups/GroupNodeViewModel.java | 3 +- .../java/org/jabref/gui/groups/GroupTree.css | 12 +- .../java/org/jabref/gui/groups/GroupTree.fxml | 38 +- .../org/jabref/gui/groups/GroupTreeView.java | 7 +- .../jabref/gui/help/AboutDialogViewModel.java | 10 +- .../gui/help/SearchForUpdateAction.java | 2 +- .../java/org/jabref/gui/icon/IconTheme.java | 21 +- .../actions/AppendDatabaseAction.java | 56 +- .../importer/actions/OpenDatabaseAction.java | 28 +- .../jabref/gui/journals/AbbreviateAction.java | 112 +++- .../gui/journals/UnabbreviateAction.java | 51 -- .../org/jabref/gui/keyboard/KeyBinding.java | 3 - .../org/jabref/gui/maintable/MainTable.java | 14 +- .../gui/maintable/OpenExternalFileAction.java | 56 ++ .../gui/maintable/OpenFolderAction.java | 43 ++ .../jabref/gui/maintable/OpenUrlAction.java | 72 +++ .../jabref/gui/maintable/RightClickMenu.java | 126 ++--- .../maintable/SearchShortScienceAction.java | 50 ++ .../gui/mergeentries/MergeEntriesAction.java | 39 +- .../MergeWithFetchedEntryAction.java | 24 +- .../jabref/gui/preferences/AppearanceTab.fxml | 8 +- .../gui/preferences/AppearanceTabView.java | 14 +- .../preferences/AppearanceTabViewModel.java | 4 + .../jabref/gui/preferences/ExternalTab.fxml | 21 - .../gui/preferences/ExternalTabView.java | 17 - .../gui/preferences/ExternalTabViewModel.java | 22 - .../jabref/gui/preferences/FileTabView.java | 4 + .../jabref/gui/preferences/GeneralTab.fxml | 2 +- .../gui/preferences/GeneralTabView.java | 10 + .../gui/preferences/NameFormatterTab.fxml | 4 +- .../gui/preferences/NameFormatterTabView.java | 24 +- .../NameFormatterTabViewModel.java | 5 +- .../gui/preferences/PreferencesDialog.fxml | 34 +- .../PreferencesDialogViewModel.java | 2 - .../gui/preferences/PreviewTabView.java | 6 +- .../gui/preferences/PreviewTabViewModel.java | 7 +- .../gui/preferences/TableColumnsTabView.java | 6 +- .../gui/preferences/XmpPrivacyTabView.java | 10 +- ...ardWorker.java => CopyCitationAction.java} | 30 +- .../shared/PullChangesFromSharedAction.java | 24 + .../gui/shared/SharedDatabaseUIManager.java | 2 +- .../gui/specialfields/SpecialFieldAction.java | 27 +- .../SpecialFieldMenuItemFactory.java | 32 +- .../SpecialFieldValueViewModel.java | 42 +- .../specialfields/SpecialFieldViewModel.java | 8 +- ...seTexAction.java => ParseLatexAction.java} | 6 +- ...seTexDialog.fxml => ParseLatexDialog.fxml} | 4 +- ...logView.java => ParseLatexDialogView.java} | 16 +- ...el.java => ParseLatexDialogViewModel.java} | 42 +- ...arseTexResult.css => ParseLatexResult.css} | 0 ...seTexResult.fxml => ParseLatexResult.fxml} | 2 +- ...ultView.java => ParseLatexResultView.java} | 12 +- ...el.java => ParseLatexResultViewModel.java} | 8 +- .../org/jabref/gui/undo/UndoRedoAction.java | 62 +++ .../jabref/gui/util/CustomLocalDragboard.java | 4 +- .../externalfiles/LinkedFileHandler.java | 32 +- .../jabref/logic/importer/WebFetchers.java | 2 + .../jabref/logic/importer/fetcher/ACS.java | 25 +- .../logic/importer/fetcher/ApsFetcher.java | 93 ++++ .../jabref/logic/importer/fetcher/ArXiv.java | 29 +- .../fetcher/AstrophysicsDataSystem.java | 3 +- .../logic/importer/fetcher/DoiResolution.java | 130 +++-- .../logic/importer/fetcher/GoogleScholar.java | 23 +- .../logic/importer/fetcher/OpenAccessDoi.java | 38 +- .../importer/fetcher/SpringerFetcher.java | 3 +- .../logic/importer/fetcher/SpringerLink.java | 36 +- .../jabref/logic/integrity/TitleChecker.java | 28 +- .../org/jabref/logic/layout/LayoutEntry.java | 6 +- ...TexParser.java => DefaultLatexParser.java} | 42 +- .../texparser/TexBibEntriesResolver.java | 16 +- .../java/org/jabref/logic/util/BuildInfo.java | 47 +- .../logic/util/ExternalLinkCreator.java | 34 ++ .../java/org/jabref/logic/util/Version.java | 9 +- .../model/cleanup/FieldFormatterCleanup.java | 4 +- .../model/database/BibDatabaseContext.java | 33 +- .../java/org/jabref/model/entry/BibEntry.java | 19 +- .../entry/identifier/ArXivIdentifier.java | 46 +- ...ava => LatexBibEntriesResolverResult.java} | 28 +- .../jabref/model/texparser/LatexParser.java | 34 ++ ...rserResult.java => LatexParserResult.java} | 8 +- .../org/jabref/model/texparser/TexParser.java | 31 -- .../jabref/preferences/JabRefPreferences.java | 157 +----- .../LastFocusedTabPreferences.java | 14 +- .../jabref/preferences/NewLineSeparator.java | 13 +- .../preferences/PreferencesService.java | 8 + src/main/resources/build.properties | 1 + .../.github/PULL_REQUEST_TEMPLATE.md | 2 +- .../resources/csl-locales/CONTRIBUTING.md | 4 +- .../csl-styles/american-chemical-society.csl | 6 +- ...titute-of-aeronautics-and-astronautics.csl | 34 +- .../csl-styles/aporia-the-nursing-journal.csl | 341 ++++++++++++ .../csl-styles/archeologicke-rozhledy.csl | 303 ++++++++++ ...ao-brasileira-de-normas-tecnicas-ufrgs.csl | 114 ++-- .../australian-guide-to-legal-citation.csl | 1 - ...beilstein-journal-of-organic-chemistry.csl | 273 +++++++++ src/main/resources/csl-styles/bibtex.csl | 2 - .../csl-styles/biophysical-journal.csl | 18 +- ...-les-civilisations-de-l-asie-orientale.csl | 74 +-- ...al-standard-gb-t-7714-2015-author-date.csl | 259 +++++++++ ...-national-standard-gb-t-7714-2015-note.csl | 302 ++++++++++ .../beilstein-journal-of-nanotechnology.csl | 15 + ...l-academy-of-physical-therapy-research.csl | 16 + .../resources/csl-styles/folia-biologica.csl | 171 ++++++ ...stes-und-kulturwissenschaften-heilmann.csl | 460 ++++++++------- ...nthropologie-et-darcheologie-funeraire.csl | 29 +- src/main/resources/csl-styles/haemophilia.csl | 215 +++++++ .../harvard-university-of-westminster.csl | 312 +++++------ ...o-de-pesquisas-energeticas-e-nucleares.csl | 13 +- ...y-linguistik-aktuell-linguistics-today.csl | 5 + .../csl-styles/journal-of-breast-cancer.csl | 251 +++++++++ .../journal-of-forensic-sciences.csl | 20 +- .../journal-of-new-zealand-grasslands.csl | 162 ++++++ .../journal-of-plankton-research.csl | 144 +++++ ...laboratory-animal-science-professional.csl | 228 ++++++++ .../csl-styles/marine-turtle-newsletter.csl | 217 ++++++++ src/main/resources/csl-styles/mcgill-en.csl | 28 +- ...forderung-und-deutsch-als-zweitsprache.csl | 4 +- .../pediatric-infectious-disease-journal.csl | 262 +++++++++ ...erican-journal-of-geriatric-psychiatry.csl | 239 ++++++++ ...of-agricultural-and-resource-economics.csl | 183 ++++++ .../the-university-of-tokyo-law-review.csl | 143 +++++ .../resources/csl-styles/transversalites.csl | 83 +-- ...evelopment-programme-icca-legal-review.csl | 325 +++++++++++ .../csl-styles/veterinaria-italiana.csl | 176 ++++++ ...odarske-technicko-ekonomicke-informace.csl | 2 +- src/main/resources/journals/journalList.csv | 3 - src/main/resources/l10n/JabRef_da.properties | 7 +- src/main/resources/l10n/JabRef_de.properties | 7 +- src/main/resources/l10n/JabRef_el.properties | 7 +- src/main/resources/l10n/JabRef_en.properties | 24 +- src/main/resources/l10n/JabRef_es.properties | 7 +- src/main/resources/l10n/JabRef_fa.properties | 4 +- src/main/resources/l10n/JabRef_fr.properties | 29 +- src/main/resources/l10n/JabRef_in.properties | 7 +- src/main/resources/l10n/JabRef_it.properties | 11 +- src/main/resources/l10n/JabRef_ja.properties | 7 +- src/main/resources/l10n/JabRef_nl.properties | 7 +- src/main/resources/l10n/JabRef_no.properties | 7 +- src/main/resources/l10n/JabRef_pl.properties | 5 +- src/main/resources/l10n/JabRef_pt.properties | 7 +- .../resources/l10n/JabRef_pt_BR.properties | 7 +- src/main/resources/l10n/JabRef_ru.properties | 7 +- src/main/resources/l10n/JabRef_sv.properties | 7 +- src/main/resources/l10n/JabRef_tl.properties | 7 +- src/main/resources/l10n/JabRef_tr.properties | 13 +- src/main/resources/l10n/JabRef_vi.properties | 7 +- src/main/resources/l10n/JabRef_zh.properties | 7 +- .../gui/exporter/SaveAllActionTest.java | 77 --- .../gui/exporter/SaveDatabaseActionTest.java | 23 +- ...rTest.java => CopyCitationActionTest.java} | 14 +- .../logic/cleanup/CleanupWorkerTest.java | 2 +- .../logic/cleanup/MoveFilesCleanupTest.java | 2 +- .../logic/cleanup/RenamePdfCleanupTest.java | 2 +- .../NormalizeMonthFormatterTest.java | 5 + .../logic/importer/FulltextFetchersTest.java | 2 +- .../logic/importer/fetcher/ACSTest.java | 11 +- .../importer/fetcher/ApsFetcherTest.java | 48 ++ .../logic/importer/fetcher/ArXivTest.java | 71 +-- .../fetcher/AstrophysicsDataSystemTest.java | 12 +- .../logic/importer/fetcher/CiteSeerTest.java | 28 +- .../importer/fetcher/DoiResolutionTest.java | 29 +- .../importer/fetcher/OpenAccessDoiTest.java | 10 + .../importer/fetcher/SpringerLinkTest.java | 10 + .../logic/integrity/IntegrityCheckTest.java | 24 +- .../logic/integrity/TitleCheckerTest.java | 79 +++ .../logic/texparser/DefaultTexParserTest.java | 60 +- ...exParserTest.java => LatexParserTest.java} | 36 +- .../texparser/TexBibEntriesResolverTest.java | 40 +- .../org/jabref/logic/util/BuildInfoTest.java | 8 +- .../logic/util/ExternalLinkCreatorTest.java | 56 ++ .../org/jabref/logic/util/VersionTest.java | 12 + .../database/BibDatabaseContextTest.java | 8 +- .../entry/identifier/ArXivIdentifierTest.java | 32 +- .../LastFocusedTabPreferencesTest.java | 6 +- src/test/resources/testbib/jabref-authors.bib | 6 + 272 files changed, 8660 insertions(+), 4114 deletions(-) create mode 100644 .github/workflows/snap.yml create mode 100644 docs/SUMMARY.md delete mode 100644 docs/debugging-jpackage-installations.md create mode 100644 docs/jpackage.md create mode 100755 snap/hooks/connect-plug-etc-chromium-native-messaging-jabref create mode 100755 snap/hooks/connect-plug-etc-opt-chrome-native-messaging-jabref rename snap/hooks/{connect-plug-browser-extension => connect-plug-hostfs-mozilla-native-messaging-jabref} (60%) create mode 100755 snap/hooks/disconnect-plug-etc-chromium-native-messaging-jabref create mode 100755 snap/hooks/disconnect-plug-etc-opt-chrome-native-messaging-jabref rename snap/hooks/{disconnect-plug-browser-extension => disconnect-plug-hostfs-mozilla-native-messaging-jabref} (99%) delete mode 100644 src/main/java/org/jabref/gui/GUIGlobals.java create mode 100644 src/main/java/org/jabref/gui/OpenConsoleAction.java rename src/main/java/org/jabref/gui/{worker => }/SendAsEMailAction.java (65%) delete mode 100644 src/main/java/org/jabref/gui/actions/Actions.java delete mode 100644 src/main/java/org/jabref/gui/actions/BaseAction.java delete mode 100644 src/main/java/org/jabref/gui/actions/OldCommandWrapper.java delete mode 100644 src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java delete mode 100644 src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java delete mode 100644 src/main/java/org/jabref/gui/customjfx/CustomJFXPanel.java rename src/main/java/org/jabref/gui/dialogs/{AutosaveUIManager.java => AutosaveUiManager.java} (81%) delete mode 100644 src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java create mode 100644 src/main/java/org/jabref/gui/edit/CopyMoreAction.java create mode 100644 src/main/java/org/jabref/gui/edit/EditAction.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/OpenEntryEditorAction.java create mode 100644 src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java create mode 100644 src/main/java/org/jabref/gui/exporter/SaveAction.java delete mode 100644 src/main/java/org/jabref/gui/journals/UnabbreviateAction.java create mode 100644 src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java create mode 100644 src/main/java/org/jabref/gui/maintable/OpenFolderAction.java create mode 100644 src/main/java/org/jabref/gui/maintable/OpenUrlAction.java create mode 100644 src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java rename src/main/java/org/jabref/gui/preview/{CitationStyleToClipboardWorker.java => CopyCitationAction.java} (89%) create mode 100644 src/main/java/org/jabref/gui/shared/PullChangesFromSharedAction.java rename src/main/java/org/jabref/gui/texparser/{ParseTexAction.java => ParseLatexAction.java} (75%) rename src/main/java/org/jabref/gui/texparser/{ParseTexDialog.fxml => ParseLatexDialog.fxml} (92%) rename src/main/java/org/jabref/gui/texparser/{ParseTexDialogView.java => ParseLatexDialogView.java} (85%) rename src/main/java/org/jabref/gui/texparser/{ParseTexDialogViewModel.java => ParseLatexDialogViewModel.java} (84%) rename src/main/java/org/jabref/gui/texparser/{ParseTexResult.css => ParseLatexResult.css} (100%) rename src/main/java/org/jabref/gui/texparser/{ParseTexResult.fxml => ParseLatexResult.fxml} (92%) rename src/main/java/org/jabref/gui/texparser/{ParseTexResultView.java => ParseLatexResultView.java} (83%) rename src/main/java/org/jabref/gui/texparser/{ParseTexResultViewModel.java => ParseLatexResultViewModel.java} (89%) create mode 100644 src/main/java/org/jabref/gui/undo/UndoRedoAction.java create mode 100644 src/main/java/org/jabref/logic/importer/fetcher/ApsFetcher.java rename src/main/java/org/jabref/logic/texparser/{DefaultTexParser.java => DefaultLatexParser.java} (83%) create mode 100644 src/main/java/org/jabref/logic/util/ExternalLinkCreator.java rename src/main/java/org/jabref/model/texparser/{TexBibEntriesResolverResult.java => LatexBibEntriesResolverResult.java} (58%) create mode 100644 src/main/java/org/jabref/model/texparser/LatexParser.java rename src/main/java/org/jabref/model/texparser/{TexParserResult.java => LatexParserResult.java} (96%) delete mode 100644 src/main/java/org/jabref/model/texparser/TexParser.java create mode 100644 src/main/resources/csl-styles/aporia-the-nursing-journal.csl create mode 100644 src/main/resources/csl-styles/archeologicke-rozhledy.csl create mode 100644 src/main/resources/csl-styles/beilstein-journal-of-organic-chemistry.csl create mode 100644 src/main/resources/csl-styles/china-national-standard-gb-t-7714-2015-author-date.csl create mode 100644 src/main/resources/csl-styles/china-national-standard-gb-t-7714-2015-note.csl create mode 100644 src/main/resources/csl-styles/dependent/beilstein-journal-of-nanotechnology.csl create mode 100644 src/main/resources/csl-styles/dependent/journal-of-international-academy-of-physical-therapy-research.csl create mode 100644 src/main/resources/csl-styles/folia-biologica.csl create mode 100644 src/main/resources/csl-styles/haemophilia.csl create mode 100644 src/main/resources/csl-styles/journal-of-breast-cancer.csl create mode 100644 src/main/resources/csl-styles/journal-of-new-zealand-grasslands.csl create mode 100644 src/main/resources/csl-styles/journal-of-plankton-research.csl create mode 100644 src/main/resources/csl-styles/laboratory-animal-science-professional.csl create mode 100644 src/main/resources/csl-styles/marine-turtle-newsletter.csl create mode 100644 src/main/resources/csl-styles/pediatric-infectious-disease-journal.csl create mode 100644 src/main/resources/csl-styles/the-american-journal-of-geriatric-psychiatry.csl create mode 100644 src/main/resources/csl-styles/the-australian-journal-of-agricultural-and-resource-economics.csl create mode 100644 src/main/resources/csl-styles/the-university-of-tokyo-law-review.csl create mode 100644 src/main/resources/csl-styles/united-nations-development-programme-icca-legal-review.csl create mode 100644 src/main/resources/csl-styles/veterinaria-italiana.csl delete mode 100644 src/test/java/org/jabref/gui/exporter/SaveAllActionTest.java rename src/test/java/org/jabref/gui/preview/{CitationStyleToClipboardWorkerTest.java => CopyCitationActionTest.java} (94%) create mode 100644 src/test/java/org/jabref/logic/importer/fetcher/ApsFetcherTest.java create mode 100644 src/test/java/org/jabref/logic/integrity/TitleCheckerTest.java rename src/test/java/org/jabref/logic/texparser/{TexParserTest.java => LatexParserTest.java} (78%) create mode 100644 src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 2bece377356..d4671771481 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - master-release paths-ignore: - 'docs/**' - 'src/test/**' @@ -14,52 +15,47 @@ on: - 'src/test/**' - 'README.md' +env: + SpringerNatureAPIKey: ${{ secrets.SpringerNatureAPIKey }} + jobs: build: strategy: - fail-fast: true + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] include: - - os: ubuntu-latest - displayName: linux - jpackageDownload: https://download.java.net/java/early_access/jdk14/34/GPL/openjdk-14-ea+34_linux-x64_bin.tar.gz - jdk14Path: /jdk-14 - archivePortable: tar -c -C build/distribution JabRef | pigz --rsyncable > build/distribution/JabRef-portable_linux.tar.gz && rm -R build/distribution/JabRef - - os: windows-latest - displayName: windows - jpackageDownload: https://download.java.net/java/early_access/jdk14/34/GPL/openjdk-14-ea+34_windows-x64_bin.zip - jdk14Path: /jdk-14 - archivePortable: 7z a -r build/distribution/JabRef-portable_windows.zip ./build/distribution/JabRef && rm -R build/distribution/JabRef - - os: macOS-latest - displayName: macOS - jpackageDownload: https://download.java.net/java/early_access/jdk14/34/GPL/openjdk-14-ea+34_osx-x64_bin.tar.gz - jdk14Path: /jdk-14.jdk/Contents/Home - archivePortable: brew install pigz && tar -c -C build/distribution JabRef.app | pigz --rsyncable > build/distribution/JabRef-portable_macos.tar.gz && rm -R build/distribution/JabRef.app + - os: ubuntu-latest + displayName: linux + jpackageDownload: https://download.java.net/java/early_access/jdk14/34/GPL/openjdk-14-ea+34_linux-x64_bin.tar.gz + jdk14Path: /jdk-14 + archivePortable: tar -c -C build/distribution JabRef | pigz --rsyncable > build/distribution/JabRef-portable_linux.tar.gz && rm -R build/distribution/JabRef + - os: windows-latest + displayName: windows + jpackageDownload: https://download.java.net/java/early_access/jdk14/34/GPL/openjdk-14-ea+34_windows-x64_bin.zip + jdk14Path: /jdk-14 + archivePortable: 7z a -r build/distribution/JabRef-portable_windows.zip ./build/distribution/JabRef && rm -R build/distribution/JabRef + - os: macOS-latest + displayName: macOS + jpackageDownload: https://download.java.net/java/early_access/jdk14/34/GPL/openjdk-14-ea+34_osx-x64_bin.tar.gz + jdk14Path: /jdk-14.jdk/Contents/Home + archivePortable: brew install pigz && tar -c -C build/distribution JabRef.app | pigz --rsyncable > build/distribution/JabRef-portable_macos.tar.gz && rm -R build/distribution/JabRef.app runs-on: ${{ matrix.os }} name: Create installer and portable version for ${{ matrix.displayName }} steps: - name: Checkout source - uses: actions/checkout@v2-beta - with: - fetch-depth: 0 - - name: Fetch tags and master for GitVersion - run: | - git fetch --tags origin - git rev-parse --verify master - if (-not $?) { - git branch --force --create-reflog master origin/master - } - shell: pwsh + uses: actions/checkout@v2 + - name: Fetch all history for all tags and branches + run: git fetch --prune --unshallow - name: Install GitVersion - uses: gittools/actions/setup-gitversion@v0.3 + uses: gittools/actions/gitversion/setup@v0.9.1 with: - versionSpec: '5.1.2' + versionSpec: '5.1.3' - name: Run GitVersion id: gitversion - uses: gittools/actions/execute-gitversion@v0.3 + uses: gittools/actions/gitversion/execute@v0.9.1 - name: Set up JDK uses: actions/setup-java@v1 with: @@ -76,8 +72,6 @@ jobs: ${{ runner.OS }}- - uses: actions/cache@v1 name: Cache gradle wrapper - # cache at Mac OS X is 2 GB (too large) - if: matrix.displayName == 'linux' || matrix.displayName == 'windows' with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} @@ -113,24 +107,9 @@ jobs: - name: Package application image run: ${{ matrix.archivePortable }} shell: bash - - name: Build snap (1) Setup snapcraft - uses: jhenstridge/snapcraft-build-action@v1 - id: snapcraft - if: matrix.displayName == 'linux' - - name: Build snap (2) Run build - run: | - mv ${{ steps.snapcraft.outputs.snap }} build/distribution/ - if: matrix.displayName == 'linux' - - name: Build snap (3) Upload snap - if: matrix.displayName == 'linux' && github.ref == 'refs/heads/master' - env: - SNAPCRAFT_LOGIN_FILE: ${{ secrets.SNAPCRAFT_LOGIN_FILE }} - run: | - mkdir .snapcraft && echo ${SNAPCRAFT_LOGIN_FILE} | base64 --decode --ignore-garbage > .snapcraft/snapcraft.cfg - snapcraft push build/distribution/jabref*.snap --release edge || true - shell: bash - name: Rename files run: | + get-childitem -Path build/distribution/* get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "${{ steps.gitversion.outputs.AssemblySemVer }}","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}"} get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "portable","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-portable"} shell: pwsh @@ -145,24 +124,16 @@ jobs: needs: [build] steps: - name: Checkout source - uses: actions/checkout@v2-beta - with: - fetch-depth: 0 - - name: Fetch tags and master for GitVersion - run: | - git fetch --tags origin - git rev-parse --verify master - if (-not $?) { - git branch --force --create-reflog master origin/master - } - shell: pwsh + uses: actions/checkout@v2 + - name: Fetch all history for all tags and branches + run: git fetch --prune --unshallow - name: Install GitVersion - uses: gittools/actions/setup-gitversion@v0.3 + uses: gittools/actions/gitversion/setup@v0.9.1 with: - versionSpec: '5.1.2' + versionSpec: '5.1.3' - name: Run GitVersion id: gitversion - uses: gittools/actions/execute-gitversion@v0.3 + uses: gittools/actions/gitversion/execute@v0.9.1 - name: Get linux binaries uses: actions/download-artifact@master with: @@ -190,3 +161,4 @@ jobs: ssh_options: '-p 9922' src: 'build/distribution/' dest: jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ + diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml new file mode 100644 index 00000000000..fe175294cbf --- /dev/null +++ b/.github/workflows/snap.yml @@ -0,0 +1,26 @@ +name: Snap + +on: + schedule: + # run on each day + - cron: '33 4 * * *' + +jobs: + build: + runs-on: ubuntu-latest + name: Create snapcraft image + + steps: + - name: Checkout source + uses: actions/checkout@v2 + # The image relies on https://builds.jabref.org/master/JabRef-5.0-portable_linux.tar.gz^ + # See snap/snapcraft.yml for details + - name: Build snap (1) Run build + uses: jhenstridge/snapcraft-build-action@v1 + id: snapcraft + - name: Build snap (2) Upload snap + uses: jhenstridge/snapcraft-publish-action@v1 + with: + store_login: ${{ secrets.SNAPCRAFT_LOGIN_FILE }} + snap: ${{ steps.snapcraft.outputs.snap }} + release: edge diff --git a/.github/workflows/tests-fetchers.yml b/.github/workflows/tests-fetchers.yml index 5cefdd1cdd3..dfdab9a3672 100644 --- a/.github/workflows/tests-fetchers.yml +++ b/.github/workflows/tests-fetchers.yml @@ -21,6 +21,9 @@ on: # run on each Wednesday - cron: '2 3 * * 3' +env: + SpringerNatureAPIKey: ${{ secrets.SpringerNatureAPIKey }} + jobs: fetchertests: name: Fetcher tests diff --git a/.github/workflows/tests-oracle.yml b/.github/workflows/tests-oracle.yml index dbaf5ddb361..a79768fe6d7 100644 --- a/.github/workflows/tests-oracle.yml +++ b/.github/workflows/tests-oracle.yml @@ -18,6 +18,9 @@ on: # run on each Wednesday - cron: '2 3 * * 3' +env: + SpringerNatureAPIKey: ${{ secrets.SpringerNatureAPIKey }} + jobs: oracletests: name: Oracle tests diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index adbebcba3c1..085ead03b89 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,16 +7,16 @@ on: pull_request: # always run on pull requests +env: + SpringerNatureAPIKey: ${{ secrets.SpringerNatureAPIKey }} + jobs: checkstyle: name: Checkstyle runs-on: ubuntu-latest steps: - name: Checkout source - uses: actions/checkout@v1 - with: - depth: 1 - submodules: false + uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: @@ -42,10 +42,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source - uses: actions/checkout@v1 - with: - depth: 1 - submodules: false + uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: @@ -89,10 +86,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Checkout source - uses: actions/checkout@v1 - with: - depth: 1 - submodules: false + uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: @@ -138,10 +132,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source - uses: actions/checkout@v1 - with: - depth: 1 - submodules: false + uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: @@ -180,10 +171,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Checkout source - uses: actions/checkout@v1 - with: - depth: 1 - submodules: false + uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: diff --git a/.gitignore b/.gitignore index 262f7cb8401..61271852342 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ status.md # Install4J install4j6/ +# JDK14 (see .github/deployment.yml for details) +jdk-14/ + # Python __pycache__/ diff --git a/.mailmap b/.mailmap index 7c614bfb627..2345c1529e1 100644 --- a/.mailmap +++ b/.mailmap @@ -186,3 +186,5 @@ Michal Rican Param Mittal Victor Michelan P4trice <34972281+P4trice@users.noreply.github.com> +Julien Bénard <50318255+Julien29121998@users.noreply.github.com> +Alexsandro Lauber diff --git a/AUTHORS b/AUTHORS index da7ac9bf114..67b6567500e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -77,6 +77,7 @@ Daniel Svärd David Gleich David Méndez David Weitzman +Dawid Motyka Dawid Owoc Deepak Kumar Dennis Tschechlov @@ -112,6 +113,7 @@ ffffatgoose Florian Beetz Florian Straßer Foivos Christoulakis +fpguy Francois Charette Frank Steimle frasca80 @@ -121,6 +123,7 @@ Galileo Sartor Geoffrey Taerim Kim Gert Renckens Gregor Herrmann +guenesaydin Guillaume Gardey Hakan Duran Hannes Restel @@ -141,6 +144,7 @@ Jeff Miller Jeffrey Kuhn Jeffrey Sander Jens Döcke +joeyzgraggen Johannes Hupe Johannes Manner John David @@ -153,6 +157,7 @@ josephshin93 Joshua Ramon Enslin José Jesús Sinohui Fernández Julian Pfeifer +Julien Bénard Jure Slak József Pallagi Jörg Lenhard @@ -182,7 +187,9 @@ Li Zhilin Ling Wang Linus Dietz Lorenzo Genta +Lucas Beretti Luciana de Melo e Abud +Lugduni Desrosiers Luis Romero Mairieli Wessel Malik Atalla @@ -203,6 +210,7 @@ Mattia Bunel Mattias Ulbrich mcmoody Meltem Demirköprü +MhhhxX Michael Beckmann Michael Falkenthal Michael Lass @@ -230,10 +238,12 @@ Nico Schlömer Nicolas Pavillon Nikita Borovikov nikmilpv +NikodemKch Niv Ierushalmi Nivedha Sunderraj Nizar N. Batada noravanq +obsluk00 Olaf Lenz Oliver Beckmann Oliver Kopp @@ -304,6 +314,7 @@ Sven Jäger systemoperator Thiago Toledo Thomas Arildsen +Thomas F. Duellmann Thomas Ilsche Thorsten Dahlheimer Tim Kilian @@ -313,6 +324,7 @@ Tobias Boceck Tobias Bouschen Tobias Denkinger Tobias Diez +Tomás Morales de Luna Tony K Toralf Senger uid112001 @@ -321,6 +333,7 @@ Ulrik Stervbo UltimaBGD Uwe Kuehn Valentin Pons +Venceslas Roullier Victor Figueira Victor Michelan Vincent W. Yang diff --git a/CHANGELOG.md b/CHANGELOG.md index 24af6f3b56c..32c7ef06b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,47 @@ # Changelog All notable changes to this project will be documented in this file. -This project **does not** adhere to [Semantic Versioning](http://semver.org/). -This file tries to follow the conventions proposed by [keepachangelog.com](http://keepachangelog.com/). -Here, the categories "Changed" for added and changed functionality, -"Fixed" for fixed functionality, and -"Removed" for removed functionality are used. - +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#NUM`. +Note that this project **does not** adhere to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- We added support for searching ShortScience for an entry through the user's browser. [#6018](https://github.com/JabRef/jabref/pull/6018) + ### Changed -- We reintroduced the possibility to extract references from plain text (using GROBID) [#5614](https://github.com/JabRef/jabref/pull/5614) +- We improved the arXiv fetcher. Now it should find entries even more reliably and does no longer include the version (e.g `v1`) in the `eprint` field. [forum#1941](https://discourse.jabref.org/t/remove-version-in-arxiv-import/1941) +- We moved the group search bar and the button "New group" from bottom to top position to make it more prominent. [#6112](https://github.com/JabRef/jabref/pull/6112) +- We changed the buttons for import/export/show all/reset of preferences to smaller icon buttons in the preferences dialog. [#6130](https://github.com/JabRef/jabref/pull/6130) +- We moved the functionality "Manage field names & content" from the "Library" menu to the "Edit" menu, because it affects the selected entries and not the whole library + +### Fixed + +- We fixed an issue where opening a library from the recent libraries menu was not possible. [#5939](https://github.com/JabRef/jabref/issues/5939) +- We fixed an issue with inconsistent capitalization of file extensions when downloading files [#6115](https://github.com/JabRef/jabref/issues/6115) +- We fixed the display of language and encoding in the preferences dialog. [#6130](https://github.com/JabRef/jabref/pull/6130) + +### Removed + +- We removed the obsolete `External programs / Open PDF` section in the preferences, as the default application to open PDFs is now set in the `Manage external file types` dialog. [#6130](https://github.com/JabRef/jabref/pull/6130) + +## [5.0] – 2020-03-06 + +### Changed + +- Added browser integration to the snap package for firefox/chromium browsers. [#6062](https://github.com/JabRef/jabref/pull/6062) +- We reintroduced the possibility to extract references from plain text (using [GROBID](https://grobid.readthedocs.io/en/latest/)). [#5614](https://github.com/JabRef/jabref/pull/5614) - We changed the open office panel to show buttons in rows of three instead of going straight down to save space as the button expanded out to take up unnecessary horizontal space. [#5479](https://github.com/JabRef/jabref/issues/5479) - We cleaned up the group add/edit dialog. [#5826](https://github.com/JabRef/jabref/pull/5826) - We reintroduced the index column. [#5844](https://github.com/JabRef/jabref/pull/5844) - Filenames of external files can no longer contain curly braces. [#5926](https://github.com/JabRef/jabref/pull/5926) - We made the filters more easily accessible in the integrity check dialog. [#5955](https://github.com/JabRef/jabref/pull/5955) +- We reimplemented and improved the dialog "Customize entry types". [#4719](https://github.com/JabRef/jabref/issues/4719) - We reimplemented and improved the dialog "Customize entry types" [#4719](https://github.com/JabRef/jabref/issues/4719) +- We added an [American Physical Society](https://journals.aps.org/) fetcher. [#818](https://github.com/JabRef/jabref/issues/818) ### Fixed @@ -38,7 +60,6 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where entries containing Unicode charaters were not parsed correctly [#5899](https://github.com/JabRef/jabref/issues/5899) - We fixed an issue where an entry containing an external filename with curly braces could not be saved. Curly braces are now longer allowed in filenames. [#5899](https://github.com/JabRef/jabref/issues/5899) - We fixed an issue where changing the type of an entry did not update the main table [#5906](https://github.com/JabRef/jabref/issues/5906) -- We fixed an issue where opening a library from the recent libraries menu was not possible [#5939](https://github.com/JabRef/jabref/issues/5939) - We fixed an issue in the optics of the library properties, that cropped the dialog on scaled displays. [#5969](https://github.com/JabRef/jabref/issues/5969) - We fixed an issue where changing the type of an entry did not update the main table. [#5906](https://github.com/JabRef/jabref/issues/5906) - We fixed an issue where opening a library from the recent libraries menu was not possible. [#5939](https://github.com/JabRef/jabref/issues/5939) @@ -47,13 +68,14 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where the group and the link column were not updated after changing the entry in the main table. [#5985](https://github.com/JabRef/jabref/issues/5985) - We fixed an issue where reordering the groups was not possible after inserting an article. [#6008](https://github.com/JabRef/jabref/issues/6008) - We fixed an issue where citation styles except the default "Preview" could not be used. [#56220](https://github.com/JabRef/jabref/issues/5622) +- We fixed an issue where a warning was displayed when the title content is made up of two sentences. [#5832](https://github.com/JabRef/jabref/issues/5832) +- We fixed an issue where an exception was thrown when adding a save action without a selected formatter in the library properties [#6069](https://github.com/JabRef/jabref/issues/6069) ### Removed - Ampersands are no longer escaped by default in the `bib` file. If you want to keep the current behaviour, you can use the new "Escape Ampersands" formatter as a save action. [#5869](https://github.com/JabRef/jabref/issues/5869) - The "Merge Entries" entry was removed from the Quality Menu. Users should use the right-click menu instead. [#6021](https://github.com/JabRef/jabref/pull/6021) - ## [5.0-beta] – 2019-12-15 ### Changed @@ -130,12 +152,12 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We removed an internal step in the [ISBN-to-BibTeX fetcher](https://docs.jabref.org/import-using-publication-identifiers/isbntobibtex): The [ISBN to BibTeX Converter](https://manas.tungare.name/software/isbn-to-bibtex) by [@manastungare](https://github.com/manastungare) is not used anymore, because it is offline: "people using this tool have not been generating enough sales for Amazon." - We removed the option to control the default drag and drop behaviour. You can use the modifier keys (like CtrL or Alt) instead. - ## [5.0-alpha] – 2019-08-25 ### Changed -- We added eventitle, eventdate and venue fields to @unpublished entry type. -- We added @software and @dataSet entry type to biblatex. + +- We added eventitle, eventdate and venue fields to `@unpublished` entry type. +- We added `@software` and `@dataSet` entry type to biblatex. - All fields are now properly sorted alphabetically (in the subgroups of required/optional fields) when the entry is written to the bib file. - We fixed an issue where some importers used the field `pubstatus` instead of the standard BibTeX field `pubstate`. - We changed the latex command removal for docbook exporter. [#3838](https://github.com/JabRef/jabref/issues/3838) @@ -154,12 +176,12 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# Because of this change, the last-used cleanup operations were reset. - We changed the minimum required version of Java to 1.8.0_171, as this is the latest release for which the automatic Java update works. [#4093](https://github.com/JabRef/jabref/issues/4093) - The special fields like `Printed` and `Read status` now show gray icons when the row is hovered. -- We added a button in the tab header which allows you to close the database with one click. https://github.com/JabRef/jabref/issues/494 -- Sorting in the main table now takes information from cross-referenced entries into account. https://github.com/JabRef/jabref/issues/2808 +- We added a button in the tab header which allows you to close the database with one click. [#494](https://github.com/JabRef/jabref/issues/494) +- Sorting in the main table now takes information from cross-referenced entries into account. [#2808](https://github.com/JabRef/jabref/issues/2808) - If a group has a color specified, then entries matched by this group have a small colored bar in front of them in the main table. - Change default icon for groups to a circle because a colored version of the old icon was hard to distinguish from its black counterpart. - In the main table, the context menu appears now when you press the "context menu" button on the keyboard. [feature request in the forum](http://discourse.jabref.org/t/how-to-enable-keyboard-context-key-windows) -- We added icons to the group side panel to quickly switch between `union` and `intersection` group view mode https://github.com/JabRef/jabref/issues/3269. +- We added icons to the group side panel to quickly switch between `union` and `intersection` group view mode. [#3269](https://github.com/JabRef/jabref/issues/3269). - We use `https` for [fetching from most online bibliographic database](https://docs.jabref.org/import-using-online-bibliographic-database). - We changed the default keyboard shortcuts for moving between entries when the entry editor is active to ̀alt + up/down. - Opening a new file now prompts the directory of the currently selected file, instead of the directory of the last opened file. @@ -171,8 +193,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We removed the redundant new lines of markings and wrapped the summary in the File annotation tab. [#3823](https://github.com/JabRef/jabref/issues/3823) - We add auto URL formatting when user paste link to URL field in entry editor. [koppor#254](https://github.com/koppor/jabref/issues/254) - We added a minimum height for the entry editor so that it can no longer be hidden by accident. [#4279](https://github.com/JabRef/jabref/issues/4279) -- We added a new keyboard shortcut so that the entry editor could be closed by Ctrl + E. [#4222] (https://github.com/JabRef/jabref/issues/4222) -- We added an option in the preference dialog box, that allows user to pick the dark or light theme option. [#4130] (https://github.com/JabRef/jabref/issues/4130) +- We added a new keyboard shortcut so that the entry editor could be closed by Ctrl + E. [#4222](https://github.com/JabRef/jabref/issues/4222) +- We added an option in the preference dialog box, that allows user to pick the dark or light theme option. [#4130](https://github.com/JabRef/jabref/issues/4130) - We updated the Related Articles tab to accept JSON from the new version of the Mr. DLib service - We added an option in the preference dialog box that allows user to choose behavior after dragging and dropping files in Entry Editor. [#4356](https://github.com/JabRef/jabref/issues/4356) - We added the ability to have an export preference where previously "File"-->"Export"/"Export selected entries" would not save the user's preference[#4495](https://github.com/JabRef/jabref/issues/4495) @@ -205,24 +227,23 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We added an option in preferences to allow for integers in field "edition" when running database in bibtex mode. [#4680](https://github.com/JabRef/jabref/issues/4680) - We added the ability to use negation in export filter layouts. [#5138](https://github.com/JabRef/jabref/pull/5138) - ### Fixed - We fixed an issue where JabRef died silently for the user without enough inotify instances [#4874](https://github.com/JabRef/jabref/issues/4847) - We fixed an issue where corresponding groups are sometimes not highlighted when clicking on entries [#3112](https://github.com/JabRef/jabref/issues/3112) - We fixed an issue where custom exports could not be selected in the 'Export (selected) entries' dialog [#4013](https://github.com/JabRef/jabref/issues/4013) -- Italic text is now rendered correctly. https://github.com/JabRef/jabref/issues/3356 -- The entry editor no longer gets corrupted after using the source tab. https://github.com/JabRef/jabref/issues/3532 https://github.com/JabRef/jabref/issues/3608 https://github.com/JabRef/jabref/issues/3616 -- We fixed multiple issues where entries did not show up after import if a search was active. https://github.com/JabRef/jabref/issues/1513 https://github.com/JabRef/jabref/issues/3219 -- We fixed an issue where the group tree was not updated correctly after an entry was changed. https://github.com/JabRef/jabref/issues/3618 -- We fixed an issue where a right-click in the main table selected a wrong entry. https://github.com/JabRef/jabref/issues/3267 -- We fixed an issue where in rare cases entries where overlayed in the main table. https://github.com/JabRef/jabref/issues/3281 -- We fixed an issue where selecting a group messed up the focus of the main table and the entry editor. https://github.com/JabRef/jabref/issues/3367 -- We fixed an issue where composite author names were sorted incorrectly. https://github.com/JabRef/jabref/issues/2828 +- Italic text is now rendered correctly. [#3356](https://github.com/JabRef/jabref/issues/3356) +- The entry editor no longer gets corrupted after using the source tab. [#3532](https://github.com/JabRef/jabref/issues/3532) [#3608](https://github.com/JabRef/jabref/issues/3608) [#3616](https://github.com/JabRef/jabref/issues/3616) +- We fixed multiple issues where entries did not show up after import if a search was active. [#1513](https://github.com/JabRef/jabref/issues/1513) [#3219](https://github.com/JabRef/jabref/issues/3219)) +- We fixed an issue where the group tree was not updated correctly after an entry was changed. [#3618](https://github.com/JabRef/jabref/issues/3618) +- We fixed an issue where a right-click in the main table selected a wrong entry. [#3267](https://github.com/JabRef/jabref/issues/3267) +- We fixed an issue where in rare cases entries where overlayed in the main table. [#3281](https://github.com/JabRef/jabref/issues/3281) +- We fixed an issue where selecting a group messed up the focus of the main table and the entry editor. [#3367](https://github.com/JabRef/jabref/issues/3367) +- We fixed an issue where composite author names were sorted incorrectly. [#2828](https://github.com/JabRef/jabref/issues/2828) - We fixed an issue where commands followed by `-` didn't work. [#3805](https://github.com/JabRef/jabref/issues/3805) - We fixed an issue where a non-existing aux file in a group made it impossible to open the library. [#4735](https://github.com/JabRef/jabref/issues/4735) - We fixed an issue where some journal names were wrongly marked as abbreviated. [#4115](https://github.com/JabRef/jabref/issues/4115) -- We fixed an issue where the custom file column were sorted incorrectly. https://github.com/JabRef/jabref/issues/3119 +- We fixed an issue where the custom file column were sorted incorrectly. [#3119](https://github.com/JabRef/jabref/issues/3119) - We improved the parsing of author names whose infix is abbreviated without a dot. [#4864](https://github.com/JabRef/jabref/issues/4864) - We fixed an issues where the entry losses focus when a field is edited and at the same time used for sorting. https://github.com/JabRef/jabref/issues/3373 - We fixed an issue where the menu on Mac OS was not displayed in the usual Mac-specific way. https://github.com/JabRef/jabref/issues/3146 @@ -230,13 +251,13 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We fixed an issue where the order of fields in customized entry types was not saved correctly. [#4033](http://github.com/JabRef/jabref/issues/4033) - We fixed an issue where renaming a group did not change the group name in the interface. [#3189](https://github.com/JabRef/jabref/issues/3189) - We fixed an issue where the groups tree of the last database was still shown even after the database was already closed. -- We fixed an issue where the "Open file dialog" may disappear behind other windows. https://github.com/JabRef/jabref/issues/3410 +- We fixed an issue where the "Open file dialog" may disappear behind other windows. [#3410](https://github.com/JabRef/jabref/issues/3410) - We fixed an issue where the number of entries matched was not updated correctly upon adding or removing an entry. [#3537](https://github.com/JabRef/jabref/issues/3537) - We fixed an issue where the default icon of a group was not colored correctly. - We fixed an issue where the first field in entry editor was not focused when adding a new entry. [#4024](https://github.com/JabRef/jabref/issues/4024) -- We reworked the "Edit file" dialog to make it resizeable and improved the workflow for adding and editing files https://github.com/JabRef/jabref/issues/2970 +- We reworked the "Edit file" dialog to make it resizeable and improved the workflow for adding and editing files [#2970](https://github.com/JabRef/jabref/issues/2970) - We fixed an issue where custom name formatters were no longer found correctly. [#3531](https://github.com/JabRef/jabref/issues/3531) -- We fixed an issue where the month was not shown in the preview https://github.com/JabRef/jabref/issues/3239. +- We fixed an issue where the month was not shown in the preview. [#3239](https://github.com/JabRef/jabref/issues/3239) - Rewritten logic to detect a second jabref instance. [#4023](https://github.com/JabRef/jabref/issues/4023) - We fixed an issue where the "Convert to BibTeX-Cleanup" moved the content of the `file` field to the `pdf` field [#4120](https://github.com/JabRef/jabref/issues/4120) - We fixed an issue where the preview pane in entry preview in preferences wasn't showing the citation style selected [#3849](https://github.com/JabRef/jabref/issues/3849) @@ -285,6 +306,9 @@ The changelog of JabRef 4.x is available at the [v4.x branch](https://github.com The changelog of JabRef 3.x is available at the [v3.8.2 tag](https://github.com/JabRef/jabref/blob/v3.8.2/CHANGELOG.md). The changelog of JabRef 2.11 and all previous versions is available as [text file in the v2.11.1 tag](https://github.com/JabRef/jabref/blob/v2.11.1/CHANGELOG). -[Unreleased]: https://github.com/JabRef/jabref/compare/v5.0-beta...HEAD +[Unreleased]: https://github.com/JabRef/jabref/compare/v5.0...HEAD +[5.0]: https://github.com/JabRef/jabref/compare/v5.0-beta...v5.0 [5.0-beta]: https://github.com/JabRef/jabref/compare/v5.0-alpha...v5.0-beta [5.0-alpha]: https://github.com/JabRef/jabref/compare/v4.3...v5.0-alpha + + diff --git a/DEVELOPERS b/DEVELOPERS index 1eaa1bf207c..8f6e0bf2623 100644 --- a/DEVELOPERS +++ b/DEVELOPERS @@ -1,5 +1,4 @@ Oliver Kopp (since 2011) -Jörg Lenhard (since 2015) Stefan Kolb (since 2015) Matthias Geiger (since 2015) Tobias Diez (since 2015) diff --git a/GitVersion.yml b/GitVersion.yml index 1047b572bdf..1c382d48a27 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -5,5 +5,5 @@ mode: ContinuousDeployment branches: master: regex: ^master - tag: beta - pre-release-weight: 30000 # 0 after stable release, 15000 before alpha release, 30000 before beta release, 50000 before stable release + tag: '' + pre-release-weight: 0 # 0 after stable release, 15000 before alpha release, 30000 before beta release, 50000 before stable release diff --git a/LICENSE.md b/LICENSE.md index 592eef7d390..0f055c8d0f6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright © 2003-2019 [JabRef Authors](https://github.com/JabRef/jabref/blob/master/AUTHORS) +Copyright © 2003-2020 [JabRef Authors](https://github.com/JabRef/jabref/blob/master/AUTHORS) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 55c60c049a3..3afd47791ff 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,14 @@ JabRef is an open-source, cross-platform citation and reference management tool. Stay on top of your literature: JabRef helps you to collect and organize sources, find the paper you need and discover the latest research. -![main table](https://www.jabref.org/img/JabRef-4-0-MainTable.png) +![main table](https://www.jabref.org/img/jabref-5-0-maintable.png) ## Features JabRef is a cross-platform application that works on Windows, Linux and Mac OS X. It is available free of charge and is actively developed. JabRef supports you in every step of your research work. -#### Collect +### Collect - Search across many online scientific catalogues like CiteSeer, CrossRef, Google Scholar, IEEEXplore, INSPIRE-HEP, Medline PubMed, MathSciNet, Springer, arXiv, and zbMATH - Import options for over 15 reference formats @@ -24,8 +24,8 @@ JabRef supports you in every step of your research work. - Fetch complete bibliographic information based on ISBN, DOI, PubMed-ID and arXiv-ID - Extract metadata from PDFs - [JabFox Firefox Add-on](https://addons.mozilla.org/en-US/firefox/addon/jabfox/) lets you import new references directly from the browser with one click - -#### Organize + +### Organize - Group your research into hierarchical collections and organize research items based on keywords/tags, search terms or your manual assignments - Advanced search and filter features @@ -35,16 +35,16 @@ JabRef supports you in every step of your research work. - Find and merge duplicates - Attach related documents: 20 different kinds of documents supported out of the box, completely customizable and extendable - Automatically rename and move associated documents according to customizable rules -- Keep track of what you read: ranking, priority, printed, quality-assured +- Keep track of what you read: ranking, priority, printed, quality-assured -#### Cite +### Cite - Native [BibTeX] and [Biblatex] support - Cite-as-you-write functionality for external applications such as Emacs, Kile, LyX, Texmaker, TeXstudio, Vim and WinEdt. - Format references in one of the many thousand built-in citation styles or create your style - Support for Word and LibreOffice/OpenOffice for inserting and formatting citations -#### Share +### Share - Many built-in export options or create your export format - Library is saved as a simple text file and thus it is easy to share with others via Dropbox and is version-control friendly @@ -55,9 +55,9 @@ JabRef supports you in every step of your research work. Fresh development builds are available at [builds.jabref.org](https://builds.jabref.org/master/). The [latest stable release is available at FossHub](https://www.fosshub.com/JabRef.html). - - Windows: JabRef offers an installer, which also adds a shortcut to JabRef to your start menu. Please also see our [Windows FAQ](https://docs.jabref.org/faq/faqwindows) - - Linux: Please see our [Installation Guide](https://docs.jabref.org/general/installation). - - Mac OS X: Please see our [Mac OS X FAQ](https://docs.jabref.org/faq/faqosx). +- Windows: JabRef offers an installer, which also adds a shortcut to JabRef to your start menu. Please also see our [Windows FAQ](https://docs.jabref.org/faq/faqwindows) +- Linux: Please see our [Installation Guide](https://docs.jabref.org/general/installation). +- Mac OS X: Please see our [Mac OS X FAQ](https://docs.jabref.org/faq/faqosx). ## Bug Reports, Suggestions, Other Feedback @@ -65,7 +65,7 @@ The [latest stable release is available at FossHub](https://www.fosshub.com/JabR We are thankful for any bug reports or other feedback. If you have ideas for new features you want to be included in JabRef, [tell us in our forum](http://discourse.jabref.org/c/features)! -If you need support in using JabRef, please read [the documentation](https://docs.jabref.org/) first and have a look at our [community forum](https://discourse.jabref.org/c/help). +If you need support in using JabRef, please read [the documentation](https://docs.jabref.org/) first, the [frequently asked questions (FAQ)](https://docs.jabref.org/faq) and also have a look at our [community forum](https://discourse.jabref.org/c/help). You can use our [GitHub issue tracker](https://github.com/JabRef/jabref/issues) to file bug reports. An explanation of donation possibilities and usage of donations is available at our [donations page](https://donations.jabref.org). @@ -79,6 +79,7 @@ An explanation of donation possibilities and usage of donations is available at > Not a programmer? [Learn how to help.](http://contribute.jabref.org) Want to be part of a free and open-source project that tens of thousands of scientists use every day? Check out the ways you can contribute, below: + - For details on how to contribute, have a look at our [guidelines for contributing](CONTRIBUTING.md). - You are welcome to contribute new features. To get your code included into JabRef, just [fork](https://help.github.com/en/articles/fork-a-repo) the JabRef repository, make your changes, and create a [pull request](https://help.github.com/en/articles/about-pull-requests). - To work on existing JabRef issues, check out our [issue tracker](https://github.com/JabRef/jabref/issues). New to open source contributing? Look for issues with the ["good first issue"](https://github.com/JabRef/jabref/labels/good%20first%20issue) label to get started. diff --git a/build.gradle b/build.gradle index 72d2f156e01..af780ad755f 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ plugins { id 'com.github.ben-manes.versions' version '0.28.0' id 'org.javamodularity.moduleplugin' version '1.5.0' id 'org.openjfx.javafxplugin' version '0.0.8' - id 'org.beryx.jlink' version '2.17.2' + id 'org.beryx.jlink' version '2.17.3' // nicer test outputs during running and completion id 'com.adarshr.test-logger' version '2.0.0' @@ -87,7 +87,6 @@ sourceSets { } repositories { - mavenLocal() jcenter() maven { url 'https://oss.sonatype.org/content/groups/public' } maven { url 'https://repository.apache.org/snapshots' } @@ -104,7 +103,7 @@ configurations { } javafx { - version = "13.0.2" + version = "14" modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing' ] } @@ -144,7 +143,7 @@ dependencies { implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.5.4' - implementation 'org.postgresql:postgresql:42.2.10' + implementation 'org.postgresql:postgresql:42.2.11' implementation ('com.oracle.ojdbc:ojdbc10:19.3.0.0') { // causing module issues @@ -170,8 +169,8 @@ dependencies { implementation 'com.jfoenix:jfoenix:9.0.9' implementation 'org.controlsfx:controlsfx:11.0.1' - implementation 'org.jsoup:jsoup:1.12.2' - implementation 'com.konghq:unirest-java:3.6.00' + implementation 'org.jsoup:jsoup:1.13.1' + implementation 'com.konghq:unirest-java:3.6.01' implementation 'org.slf4j:slf4j-api:2.0.0-alpha1' implementation group: 'org.apache.logging.log4j', name: 'log4j-jcl', version: '3.0.0-SNAPSHOT' @@ -205,15 +204,15 @@ dependencies { testImplementation 'net.bytebuddy:byte-buddy-parent:1.10.8' testRuntime group: 'org.apache.logging.log4j', name: 'log4j-core', version: '3.0.0-SNAPSHOT' testRuntime group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '3.0.0-SNAPSHOT' - testImplementation 'org.mockito:mockito-core:3.3.1' - testImplementation 'org.xmlunit:xmlunit-core:2.6.3' - testImplementation 'org.xmlunit:xmlunit-matchers:2.6.3' + testImplementation 'org.mockito:mockito-core:3.3.3' + testImplementation 'org.xmlunit:xmlunit-core:2.6.4' + testImplementation 'org.xmlunit:xmlunit-matchers:2.6.4' testImplementation 'com.tngtech.archunit:archunit-junit5-api:0.13.1' testImplementation "org.testfx:testfx-core:4.0.17-alpha-SNAPSHOT" testImplementation "org.testfx:testfx-junit5:4.0.17-alpha-SNAPSHOT" testImplementation "org.hamcrest:hamcrest-library:2.2" - checkstyle 'com.puppycrawl.tools:checkstyle:8.29' + checkstyle 'com.puppycrawl.tools:checkstyle:8.30' xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '2.3.2' jython 'org.python:jython-standalone:2.7.1' } @@ -238,7 +237,7 @@ dependencyUpdates.resolutionStrategy = { } } rules.withModule("org.python:jython-standalone") { ComponentSelection selection -> - if (selection.candidate.version ==~ /2.7.2b\d/) { + if (selection.candidate.version ==~ /2.7.2b\d/ || selection.candidate.version ==~ /2.7.2rc\d/) { selection.reject('Release candidate') } } @@ -285,6 +284,7 @@ processResources { "authors": new File('AUTHORS').readLines().findAll { !it.startsWith("#") }.join(", "), "developers": new File('DEVELOPERS').readLines().findAll { !it.startsWith("#") }.join(", "), "azureInstrumentationKey": System.getenv('AzureInstrumentationKey'), + "springerNatureAPIKey": System.getenv('SpringerNatureAPIKey'), "minRequiredJavaVersion": minRequiredJavaVersion, "allowJava9": allowJava9 @@ -604,6 +604,7 @@ jlink { uses 'com.airhacks.afterburner.injection.PresenterFactory' uses 'com.oracle.truffle.js.runtime.builtins.JSFunctionLookup' uses 'org.apache.logging.log4j.spi.Provider' + uses 'org.mariadb.jdbc.credential.CredentialPlugin' uses 'org.mariadb.jdbc.tls.TlsSocketPlugin' uses 'org.mariadb.jdbc.LocalInfileInterceptor' uses 'javax.xml.bind.JAXBContextFactory' diff --git a/codecov.yml b/codecov.yml index 78fd542fe8a..66e0dca3e38 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,4 +6,6 @@ coverage: project: default: threshold: 0.005 -comment: off +comment: + branches: + - "do-not-add-comment-to-any-branch-please" diff --git a/docs/README.md b/docs/README.md index efaebcf04e8..4d9badc4676 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,16 +1,15 @@ # Develop JabRef -This page presents all development informatation around JabRef. For users documentation see . +This page presents all development informatation around JabRef. For users documentation see [https://docs.jabref.org](https://docs.jabref.org). ## Excersises -Uni Basel offers a German (🇩🇪) Software Engineering course which uses JabRef as one example. -Look at [Exercise 5](https://github.com/unibas-marcelluethi/software-engineering/blob/master/docs/week5/exercises/practical-exercises.md) for an exercise where some important points of JabRef are touched. +Uni Basel offers a German \(🇩🇪\) Software Engineering course which uses JabRef as one example. Look at [Exercise 5](https://github.com/unibas-marcelluethi/software-engineering/blob/master/docs/week5/exercises/practical-exercises.md) for an exercise where some important points of JabRef are touched. ## How tos -- External: [Sync your fork with the JabRef repository](https://help.github.com/articles/syncing-a-fork/) -- External (🇩🇪): Branches and pull requests: +* External: [Sync your fork with the JabRef repository](https://help.github.com/articles/syncing-a-fork/) +* External \(🇩🇪\): Branches and pull requests: [https://github.com/unibas-marcelluethi/software-engineering/blob/master/docs/week2/exercises/practical-exercises.md](https://github.com/unibas-marcelluethi/software-engineering/blob/master/docs/week2/exercises/practical-exercises.md) ## Command Line @@ -18,34 +17,29 @@ The package `org.jabref.cli` is responsible for handling the command line option During development, one can configure IntelliJ to pass command line paramters: -![IntelliJ-run-configuration](images/intellij-run-configuration-command-line.png) +![IntelliJ-run-configuration](.gitbook/assets/intellij-run-configuration-command-line%20%282%29.png) -Passing command line arguments using gradle is currently not possible as all arguments (such as `-Dfile.encoding=windows-1252`) are passed to the application. +Passing command line arguments using gradle is currently not possible as all arguments \(such as `-Dfile.encoding=windows-1252`\) are passed to the application. Without jlink, it is not possible to generate a fat jar any more. During development, the capabilities of the IDE has to be used. ## Groups -UML diagram showing aspects of groups: [Groups.uml](Gropus.uml). +UML diagram showing aspects of groups: [Groups.uml](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/Gropus.uml). ## Decision Records This log lists the decisions for JabRef. - +* [ADR-0000](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records +* [ADR-0001](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0001-use-crowdin-for-translations.md) - Use Crowdin for translations +* [ADR-0002](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0002-use-slf4j-for-logging.md) - Use slf4j together with log4j2 for logging +* [ADR-0003](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0003-use-gradle-as-build-tool.md) - Use Gradle as build tool +* [ADR-0003](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0003-use-openjson-as-replacement-for-org-json.md) - Use openjson as replacement for org.json +* [ADR-0004](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0004-use-mariadb-connector.md) - Use MariaDB Connector +* [ADR-0005](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0005-fully-support-utf8-only-for-latex-files.md) - Fully Support UTF-8 Only For LaTeX Files +* [ADR-0006](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0006-only-translated-strings-in-language-file.md) - Only translated strings in language file +* [ADR-0007](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/0007-human-readable-changelog.md) - Provide a human-readable changelog -- [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records -- [ADR-0001](0001-use-crowdin-for-translations.md) - Use Crowdin for translations -- [ADR-0002](0002-use-slf4j-for-logging.md) - Use slf4j together with log4j2 for logging -- [ADR-0003](0003-use-gradle-as-build-tool.md) - Use Gradle as build tool -- [ADR-0003](0003-use-openjson-as-replacement-for-org-json.md) - Use openjson as replacement for org.json -- [ADR-0004](0004-use-mariadb-connector.md) - Use MariaDB Connector -- [ADR-0005](0005-fully-support-utf8-only-for-latex-files.md) - Fully Support UTF-8 Only For LaTeX Files -- [ADR-0006](0006-only-translated-strings-in-language-file.md) - Only translated strings in language file -- [ADR-0007](0007-human-readable-changelog.md) - Provide a human-readable changelog +For new ADRs, please use [docs/template.md](https://github.com/JabRef/jabref/tree/ec47f2138b0550a4622872d455902443cd56d9cc/docs/docs/template.md) as basis. More information on MADR is available at [https://adr.github.io/madr/](https://adr.github.io/madr/). General information about architectural decision records is available at [https://adr.github.io/](https://adr.github.io/). - - -For new ADRs, please use [docs/template.md](docs/template.md) as basis. -More information on MADR is available at . -General information about architectural decision records is available at . diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 00000000000..153444c4db2 --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,18 @@ +# Table of contents + +* [Overview on Developing](README.md) +* [Architectural Decisions](adr/README.md) +* Getting into the code + * [Set up a local workspace](guidelines-for-setting-up-a-local-workspace.md) + * [High-level documentation](high-level-documentation.md) + * [How to test](testing.md) + * [Code Howtos](code-howtos.md) + * [JabRef's development strategy](development-strategy.md) +* Advanced reading + * [Code Quality](code-quality.md) + * [Recommendations for UI design](ui-recommendations.md) + * [Custom SVG icons](custom-svg-icons.md) + * [Creating a binary and debug it](jpackage.md) +* Readings on Coding + * [Readings on JavaFX](javafx.md) + * [Useful development tooling](tools.md) diff --git a/docs/adr/0000-use-markdown-architectural-decision-records.md b/docs/adr/0000-use-markdown-architectural-decision-records.md index 1aab9e567f7..b3e14b61a41 100644 --- a/docs/adr/0000-use-markdown-architectural-decision-records.md +++ b/docs/adr/0000-use-markdown-architectural-decision-records.md @@ -2,15 +2,14 @@ ## Context and Problem Statement -We want to record architectural decisions made in this project. -Which format and structure should these records follow? +We want to record architectural decisions made in this project. Which format and structure should these records follow? ## Considered Options * [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records * [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" * [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements -* Other templates listed at +* Other templates listed at [https://github.com/joelparkerhenderson/architecture\_decision\_record](https://github.com/joelparkerhenderson/architecture_decision_record) * Formless – No conventions for file format and structure ## Decision Outcome @@ -18,9 +17,13 @@ Which format and structure should these records follow? Chosen option: "MADR 2.1.2", because * Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). + * The MADR format is lean and fits our development style. * The MADR structure is comprehensible and facilitates usage & maintenance. * The MADR project is vivid. * Version 2.1.2 is the latest one available when starting to document ADRs. + diff --git a/docs/adr/0001-use-crowdin-for-translations.md b/docs/adr/0001-use-crowdin-for-translations.md index 5b53d44fad6..7287b27a264 100644 --- a/docs/adr/0001-use-crowdin-for-translations.md +++ b/docs/adr/0001-use-crowdin-for-translations.md @@ -14,3 +14,4 @@ The JabRef UI is offered in multiple languages. It should be easy for translator ## Decision Outcome Chosen option: "Use Crowdin", because Crowdin is easy to use, integrates in our GitHub workflow, and is free for OSS projects. + diff --git a/docs/adr/0002-use-slf4j-for-logging.md b/docs/adr/0002-use-slf4j-for-logging.md index b17996fe59d..d85a8a74887 100644 --- a/docs/adr/0002-use-slf4j-for-logging.md +++ b/docs/adr/0002-use-slf4j-for-logging.md @@ -2,8 +2,7 @@ ## Context and Problem Statement -Up to version 4.1 JabRef uses apache-commons-logging 1.2 for logging errors and messages. -However, this is not compatible with java 9 and is superseded by log4j. +Up to version 4.1 JabRef uses apache-commons-logging 1.2 for logging errors and messages. However, this is not compatible with java 9 and is superseded by log4j. ## Decision Drivers @@ -18,7 +17,7 @@ However, this is not compatible with java 9 and is superseded by log4j. ## Decision Outcome -Chosen option: "SLF4J with Log4j2 binding", because comes out best (see below). +Chosen option: "SLF4J with Log4j2 binding", because comes out best \(see below\). ## Pros and Cons of the Options @@ -46,5 +45,3 @@ Chosen option: "SLF4J with Log4j2 binding", because comes out best (see below). * Bad, because Java 9 support only available in alpha * Bad, because different syntax than log4j/commons logging - - diff --git a/docs/adr/0003-use-gradle-as-build-tool.md b/docs/adr/0003-use-gradle-as-build-tool.md index 6484e752df3..81000f31444 100644 --- a/docs/adr/0003-use-gradle-as-build-tool.md +++ b/docs/adr/0003-use-gradle-as-build-tool.md @@ -57,4 +57,5 @@ Chosen option: "Gradle", because it is lean and fits our development style. ## Links -* GADR: +* GADR: [https://github.com/adr/gadr-java/blob/master/gadr-java--build-tool.md](https://github.com/adr/gadr-java/blob/master/gadr-java--build-tool.md) + diff --git a/docs/adr/0004-use-mariadb-connector.md b/docs/adr/0004-use-mariadb-connector.md index b7d361f7e6c..2085113a0a3 100644 --- a/docs/adr/0004-use-mariadb-connector.md +++ b/docs/adr/0004-use-mariadb-connector.md @@ -2,19 +2,18 @@ ## Context and Problem Statement -JabRef needs to connect to a MySQL database. -See [Shared SQL Database](https://docs.jabref.org/collaborative-work/sqldatabase) for more information. +JabRef needs to connect to a MySQL database. See [Shared SQL Database](https://docs.jabref.org/collaborative-work/sqldatabase) for more information. ## Considered Options * Use MariaDB Connector * Use MySQL Connector -Other alternatives are listed at . +Other alternatives are listed at [https://stackoverflow.com/a/31312280/873282](https://stackoverflow.com/a/31312280/873282). ## Decision Outcome -Chosen option: "Use MariaDB Connector", because comes out best (see below). +Chosen option: "Use MariaDB Connector", because comes out best \(see below\). ## Pros and Cons of the Options @@ -26,9 +25,8 @@ The [MariaDB Connector](https://mariadb.com/kb/en/library/about-mariadb-connecto ### Use MySQL Connector -The [MySQL Connector](https://www.mysql.com/de/products/connector/) is distributed by Oracle and licensed under GPL-2. Source: . -Oracle added the [Universal FOSS Exception, Version 1.0](https://oss.oracle.com/licenses/universal-foss-exception/) to it, which seems to limit the effects of GPL. -More information on the FOSS Exception are available at . +The [MySQL Connector](https://www.mysql.com/de/products/connector/) is distributed by Oracle and licensed under GPL-2. Source: [https://downloads.mysql.com/docs/licenses/connector-j-8.0-gpl-en.pdf](https://downloads.mysql.com/docs/licenses/connector-j-8.0-gpl-en.pdf). Oracle added the [Universal FOSS Exception, Version 1.0](https://oss.oracle.com/licenses/universal-foss-exception/) to it, which seems to limit the effects of GPL. More information on the FOSS Exception are available at [https://www.mysql.com/de/about/legal/licensing/foss-exception/](https://www.mysql.com/de/about/legal/licensing/foss-exception/). * Good, because it stems from the same development team than MySQL * Bad, because the "Universal FOSS Exception" makes licensing more complicated. + diff --git a/docs/adr/0005-fully-support-utf8-only-for-latex-files.md b/docs/adr/0005-fully-support-utf8-only-for-latex-files.md index 337511fe0c4..a4d3d736710 100644 --- a/docs/adr/0005-fully-support-utf8-only-for-latex-files.md +++ b/docs/adr/0005-fully-support-utf8-only-for-latex-files.md @@ -2,18 +2,17 @@ ## Context and Problem Statement -The feature [search for citations](https://github.com/JabRef/user-documentation/issues/210) displays the content of LaTeX files. -The LaTeX files are text files and might be encoded arbitrarily. +The feature [search for citations](https://github.com/JabRef/user-documentation/issues/210) displays the content of LaTeX files. The LaTeX files are text files and might be encoded arbitrarily. ## Considered Options * Support UTF-8 encoding only * Support ASCII encoding only -* Support (nearly) all encodings +* Support \(nearly\) all encodings ## Decision Outcome -Chosen option: "Support UTF-8 encoding only", because comes out best (see below). +Chosen option: "Support UTF-8 encoding only", because comes out best \(see below\). ### Positive Consequences @@ -36,9 +35,12 @@ Chosen option: "Support UTF-8 encoding only", because comes out best (see below) * Good, because easy to implement * Bad, because does not support any encoding at all -### Support (nearly) all encodings +### Support \(nearly\) all encodings * Good, because easy to implement * Bad, because it relies on Apache Tika's `CharsetDetector`, which resides in `tika-parsers`. - This causes issues during compilation (see https://github.com/JabRef/jabref/pull/3421#issuecomment-524532832). + + This causes issues during compilation \(see [https://github.com/JabRef/jabref/pull/3421\#issuecomment-524532832](https://github.com/JabRef/jabref/pull/3421#issuecomment-524532832)\). + Example: `error: module java.xml.bind reads package javax.activation from both java.activation and jakarta.activation`. + diff --git a/docs/adr/0006-only-translated-strings-in-language-file.md b/docs/adr/0006-only-translated-strings-in-language-file.md index 9c888cae803..1aada8966eb 100644 --- a/docs/adr/0006-only-translated-strings-in-language-file.md +++ b/docs/adr/0006-only-translated-strings-in-language-file.md @@ -2,15 +2,13 @@ ## Context and Problem Statement -JabRef has translation files `JabRef_it.properties`, ... -There are translated and unstranslated strings. -Which ones should be in the translation file? +JabRef has translation files `JabRef_it.properties`, ... There are translated and unstranslated strings. Which ones should be in the translation file? ## Decision Drivers * Translators should find new strings to translate easily * New strings to translate should be written into `JabRef_en.properties` to enable translation by the translators -* Crowdin should be kept as translation platform, because 1) it is much easier for the translators than the GitHub workflow and 2) it is free for OSS projects. +* Crowdin should be kept as translation platform, because 1\) it is much easier for the translators than the GitHub workflow and 2\) it is free for OSS projects. ## Considered Options @@ -20,7 +18,7 @@ Which ones should be in the translation file? ## Decision Outcome -Chosen option: "Only translated strings in language file", because comes out best (see below. +Chosen option: "Only translated strings in language file", because comes out best \(see below. ## Pros and Cons of the Options @@ -28,7 +26,7 @@ Chosen option: "Only translated strings in language file", because comes out bes * Good, because Crowdin supports it * Bad, because translators need tooling to see untranslated strings -* Bad, because issues with FXML (https://github.com/JabRef/jabref/issues/3796) +* Bad, because issues with FXML \([https://github.com/JabRef/jabref/issues/3796](https://github.com/JabRef/jabref/issues/3796)\) ### Translated and untranslated strings in language file, have value the untranslated string to indicate untranslated @@ -39,9 +37,10 @@ Chosen option: "Only translated strings in language file", because comes out bes ### Translated and untranslated strings in language file, have empty to indicate untranslated * Good, because untranslated strings can be identified easily -* Good, because works with FMXL (?) +* Good, because works with FMXL \(?\) * Bad, because Crowdin does not support it ## Links * Related to [ADR-0001](0001-use-crowdin-for-translations.md). + diff --git a/docs/adr/0007-human-readable-changelog.md b/docs/adr/0007-human-readable-changelog.md index 76c98fd8aa4..a77d412b602 100644 --- a/docs/adr/0007-human-readable-changelog.md +++ b/docs/adr/0007-human-readable-changelog.md @@ -2,8 +2,7 @@ ## Context and Problem Statement -Changes of a release have to be communicated. -How and which stile to use? +Changes of a release have to be communicated. How and which stile to use? ## Considered Options @@ -14,9 +13,11 @@ How and which stile to use? Chosen option: "Keep-a-changelog format with freedom in the bullet points", because -- [Keep-a-changelog](https://keepachangelog.com/) structures the changelog -- Each entry can be structured to be understandable -- Forcing to prefix each line with `We fixed`, `We changed`, ... seems to be read strange. +* [Keep-a-changelog](https://keepachangelog.com/) structures the changelog +* Each entry can be structured to be understandable +* Forcing to prefix each line with `We fixed`, `We changed`, ... seems to be read strange. + We nevertheless try to follow that style. -Further discussion can be found at [#2277](https://github.com/JabRef/jabref/issues/2277). +Further discussion can be found at [\#2277](https://github.com/JabRef/jabref/issues/2277). + diff --git a/docs/adr/README.md b/docs/adr/README.md index ed0d7f65669..fbbb48cbc28 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -2,6 +2,5 @@ This directory contains the architectural decisions for JabRef. -For new ADRs, please use [template.md](template.md) as basis. -More information on the used format is available at . -General information about architectural decision records is available at . +For new ADRs, please use [template.md](template.md) as basis. More information on the used format is available at [https://adr.github.io/madr/](https://adr.github.io/madr/). General information about architectural decision records is available at [https://adr.github.io/](https://adr.github.io/). + diff --git a/docs/adr/template.md b/docs/adr/template.md index 25696bbe7c8..22d77697e71 100644 --- a/docs/adr/template.md +++ b/docs/adr/template.md @@ -1,72 +1,73 @@ -# [short title of solved problem and solution] +# \[short title of solved problem and solution\] -* Status: [proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)] -* Deciders: [list everyone involved in the decision] -* Date: [YYYY-MM-DD when the decision was last updated] +* Status: \[proposed \| rejected \| accepted \| deprecated \| … \| superseded by [ADR-0005](https://github.com/JabRef/jabref/tree/8c07a88a823a84aebe987cdb717f318ed00a872d/docs/adr/0005-example.md)\] +* Deciders: \[list everyone involved in the decision\] +* Date: \[YYYY-MM-DD when the decision was last updated\] -Technical Story: [description | ticket/issue URL] +Technical Story: \[description \| ticket/issue URL\] ## Context and Problem Statement -[Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.] +\[Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.\] -## Decision Drivers +## Decision Drivers -* [driver 1, e.g., a force, facing concern, …] -* [driver 2, e.g., a force, facing concern, …] -* … +* \[driver 1, e.g., a force, facing concern, …\] +* \[driver 2, e.g., a force, facing concern, …\] +* … ## Considered Options -* [option 1] -* [option 2] -* [option 3] -* … +* \[option 1\] +* \[option 2\] +* \[option 3\] +* … ## Decision Outcome -Chosen option: "[option 1]", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | … | comes out best (see below)]. +Chosen option: "\[option 1\]", because \[justification. e.g., only option, which meets k.o. criterion decision driver \| which resolves force force \| … \| comes out best \(see below\)\]. -### Positive Consequences +### Positive Consequences -* [e.g., improvement of quality attribute satisfaction, follow-up decisions required, …] +* \[e.g., improvement of quality attribute satisfaction, follow-up decisions required, …\] * … -### Negative Consequences +### Negative Consequences -* [e.g., compromising quality attribute, follow-up decisions required, …] +* \[e.g., compromising quality attribute, follow-up decisions required, …\] * … -## Pros and Cons of the Options +## Pros and Cons of the Options -### [option 1] +### \[option 1\] -[example | description | pointer to more information | …] +\[example \| description \| pointer to more information \| …\] -* Good, because [argument a] -* Good, because [argument b] -* Bad, because [argument c] -* … +* Good, because \[argument a\] +* Good, because \[argument b\] +* Bad, because \[argument c\] +* … -### [option 2] +### \[option 2\] -[example | description | pointer to more information | …] +\[example \| description \| pointer to more information \| …\] -* Good, because [argument a] -* Good, because [argument b] -* Bad, because [argument c] -* … +* Good, because \[argument a\] +* Good, because \[argument b\] +* Bad, because \[argument c\] +* … -### [option 3] +### \[option 3\] -[example | description | pointer to more information | …] +\[example \| description \| pointer to more information \| …\] -* Good, because [argument a] -* Good, because [argument b] -* Bad, because [argument c] -* … +* Good, because \[argument a\] +* Good, because \[argument b\] +* Bad, because \[argument c\] +* … -## Links +## Links + +* \[Link type\] \[Link to ADR\] +* … -* [Link type] [Link to ADR] -* … diff --git a/docs/code-howtos.md b/docs/code-howtos.md index df0121f8b13..764ff530f3d 100644 --- a/docs/code-howtos.md +++ b/docs/code-howtos.md @@ -6,11 +6,11 @@ This page provides some development support in the form of howtos. See also [Hig We really recommend reading the book [Java by Comparison](http://java.by-comparison.com/). -Please read https://github.com/cxxr/better-java +Please read [https://github.com/cxxr/better-java](https://github.com/cxxr/better-java) -- try not to abbreviate names of variables, classes or methods -- use lowerCamelCase instead of snake_case -- name enums in singular, e.g. `Weekday` instead of `Weekdays` (except if they represent flags) +* try not to abbreviate names of variables, classes or methods +* use lowerCamelCase instead of snake\_case +* name enums in singular, e.g. `Weekday` instead of `Weekdays` \(except if they represent flags\) ## Error Handling in JabRef @@ -18,9 +18,10 @@ Please read https://github.com/cxxr/better-java Principles: -- All Exceptions we throw should be or extend `JabRefException`; This is especially important if the message stored in the Exception should be shown to the user. `JabRefException` has already implemented the `getLocalizedMessage()` method which should be used for such cases (see details below!). -- Catch and wrap all API exceptions (such as `IOExceptions`) and rethrow them - - Example: +* All Exceptions we throw should be or extend `JabRefException`; This is especially important if the message stored in the Exception should be shown to the user. `JabRefException` has already implemented the `getLocalizedMessage()` method which should be used for such cases \(see details below!\). +* Catch and wrap all API exceptions \(such as `IOExceptions`\) and rethrow them + * Example: + ```java try { // ... @@ -29,39 +30,36 @@ Principles: Localization.lang("Something went wrong...", ioe); } ``` -- Never, ever throw and catch `Exception` or `Throwable` -- Errors should only be logged when they are finally caught (i.e., logged only once). See **Logging** for details. -- If the Exception message is intended to be shown to the User in the UI (see below) provide also a localizedMessage (see `JabRefException`). +* Never, ever throw and catch `Exception` or `Throwable` +* Errors should only be logged when they are finally caught \(i.e., logged only once\). See **Logging** for details. +* If the Exception message is intended to be shown to the User in the UI \(see below\) provide also a localizedMessage \(see `JabRefException`\). -*(Rationale and further reading: https://www.baeldung.com/java-exceptions)* +_\(Rationale and further reading:_ [https://www.baeldung.com/java-exceptions](https://www.baeldung.com/java-exceptions)_\)_ ### Outputting Errors in the UI -Principle: Error messages shown to the User should not contain technical details (e.g., underlying exceptions, or even stack traces). Instead, the message should be concise, understandable for non-programmers and localized. The technical reasons (and stack traces) for a failure should only be logged. +Principle: Error messages shown to the User should not contain technical details \(e.g., underlying exceptions, or even stack traces\). Instead, the message should be concise, understandable for non-programmers and localized. The technical reasons \(and stack traces\) for a failure should only be logged. To show error message two different ways are usually used in JabRef: -- showing an error dialog -- updating the status bar at the bottom of the main window +* showing an error dialog +* updating the status bar at the bottom of the main window -*TODO: Usage of status bar and Swing Dialogs* +_TODO: Usage of status bar and Swing Dialogs_ ## Using the EventSystem ### What the EventSystem is used for? -Many times there is a need to provide an object on many locations simultaneously. -This design pattern is quite similar to Java's Observer, but it is much simplier and readable while having the same functional sense. +Many times there is a need to provide an object on many locations simultaneously. This design pattern is quite similar to Java's Observer, but it is much simplier and readable while having the same functional sense. ### Main principle -`EventBus` represents a communication line between multiple components. -Objects can be passed through the bus and reach the listening method of another object which is registered on that `EventBus` instance. -Hence the passed object is available as a parameter in the listening method. +`EventBus` represents a communication line between multiple components. Objects can be passed through the bus and reach the listening method of another object which is registered on that `EventBus` instance. Hence the passed object is available as a parameter in the listening method. ### Register to the `EventBus` -Any listening method has to be annotated with `@Subscribe` keyword and must have only one accepting parameter. Furthermore the object which contains such listening method(s) has to be registered using the `register(Object)` method provided by `EventBus`. The listening methods can be overloaded by using differnt parameter types. +Any listening method has to be annotated with `@Subscribe` keyword and must have only one accepting parameter. Furthermore the object which contains such listening method\(s\) has to be registered using the `register(Object)` method provided by `EventBus`. The listening methods can be overloaded by using differnt parameter types. ### Posting an object @@ -113,21 +111,21 @@ public class Main { The `event` package contains some specific events which occure in JabRef. -For example: Every time an entry was added to the database a new `EntryAddedEvent` is sent trough the `eventBus` which is located in `BibDatabase`. +For example: Every time an entry was added to the database a new `EntryAddedEvent` is sent trough the `eventBus` which is located in `BibDatabase`. If you want to catch the event you'll have to register your listener class with the `registerListener(Object listener)` method in `BibDatabase`. `EntryAddedEvent` provides also methods to get the inserted `BibEntry`. ## Logging -JabRef uses the logging facade [SLF4j](https://www.slf4j.org/). -All log messages are passed internally to [log4j2](https://logging.apache.org/log4j/2.x/) which handles any filtering, formatting and writing of log messages. -- Obtaining a logger for a class: +JabRef uses the logging facade [SLF4j](https://www.slf4j.org/). All log messages are passed internally to [log4j2](https://logging.apache.org/log4j/2.x/) which handles any filtering, formatting and writing of log messages. + +* Obtaining a logger for a class: ```java private static final Log LOGGER = LogFactory.getLog(.class); ``` -- If the logging event is caused by an exception, please add the exception to the log message as: +* If the logging event is caused by an exception, please add the exception to the log message as: ```java catch (SomeException e) { @@ -136,16 +134,15 @@ All log messages are passed internally to [log4j2](https://logging.apache.org/lo } ``` -- SLF4J also support parameterized logging, e.g. if you want to print out multiple arguments in a log statement use a pair of curly braces. [Examples](https://www.slf4j.org/faq.html#logging_performance) +* SLF4J also support parameterized logging, e.g. if you want to print out multiple arguments in a log statement use a pair of curly braces. [Examples](https://www.slf4j.org/faq.html#logging_performance) ## Using Localization correctly -*(More information about this topic from the translator side is provided under this link: [Translating JabRef Interface](https://github.com/JabRef/jabref/wiki/Translating-JabRef-Interface) about the JabRef interface and -[Translating JabRef Help](https://github.com/JabRef/jabref/wiki/Translating-JabRef-Help) about the JabRef help files)* +_\(More information about this topic from the translator side is provided under this link:_ [_Translating JabRef Interface_](https://github.com/JabRef/jabref/wiki/Translating-JabRef-Interface) _about the JabRef interface and_ [_Translating JabRef Help_](https://github.com/JabRef/jabref/wiki/Translating-JabRef-Help) _about the JabRef help files\)_ All labeled UI elements, descriptions and messages shown to the user should be localized, i.e., should be displayed in the chosen language. -JabRef uses ResourceBundles ([see Oracle Tutorial](https://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html)) to store `key=value` pairs for each String to be localized. +JabRef uses ResourceBundles \([see Oracle Tutorial](https://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html)\) to store `key=value` pairs for each String to be localized. To show an localized String the following `org.jabref.logic.l10n.Localization` has to be used. The Class currently provides three methods to obtain translated strings: @@ -167,16 +164,16 @@ The actual usage might look like: General hints: -- Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")` -- Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");` -- Use a full stop/period (".") to end full sentences +* Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` \(possibly in the form `someVariable = Localization.lang("Translate me")` +* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");` +* Use a full stop/period \("."\) to end full sentences The tests check whether translation strings appear correctly in the resource bundles. 1. Add new `Localization.lang("KEY")` to Java file. 2. Tests fail. In the test output a snippet is generated which must be added to the English translation file. There is also a snippet generated for the non-English files, but this is irrelevant. 3. Add snippet to English translation file located at `src/main/resources/l10n/JabRef_en.properties` -4. With `gradlew localizationUpdate` the "KEY" is added to the other translation files as well (you can use `gradlew localizationUpdateExtended` for extended output). [Crowdin](http://translate.jabref.org) will add them as required. +4. With `gradlew localizationUpdate` the "KEY" is added to the other translation files as well \(you can use `gradlew localizationUpdateExtended` for extended output\). [Crowdin](http://translate.jabref.org) will add them as required. 5. Tests are green again. ## Cleanup and Formatters @@ -193,8 +190,7 @@ For accessing or putting data into the Clipboard use the `ClipboardManager`. ## Get the JabRef frame panel -`JabRefFrame` and `BasePanel` are the two main classes. -You should never directly call them, instead pass them as parameters to the class. +`JabRefFrame` and `BasePanel` are the two main classes. You should never directly call them, instead pass them as parameters to the class. ## Get Absolute Filename or Path for file in File directory @@ -202,54 +198,58 @@ You should never directly call them, instead pass them as parameters to the clas Optional file = FileHelper.expandFilename(database, fileText, preferences.getFilePreferences()); ``` -`String path` Can be the files name or a relative path to it. -The Preferences should only be directly accessed in the GUI. For the usage in logic pass them as parameter +`String path` Can be the files name or a relative path to it. The Preferences should only be directly accessed in the GUI. For the usage in logic pass them as parameter ## Setting a Database Directory for a .bib File * `@comment{jabref-meta: fileDirectory:` -* “fileDirectory” is determined by Globals.pref.get(“userFileDir”) (which defaults to “fileDirectory” -* There is also “fileDirectory-<username>”, which is determined by Globals.prefs.get(“userFileDirIndividual”) +* “fileDirectory” is determined by Globals.pref.get\(“userFileDir”\) \(which defaults to “fileDirectory” +* There is also “fileDirectory-<username>”, which is determined by Globals.prefs.get\(“userFileDirIndividual”\) * Used at DatabasePropertiesDialog ## How to work with Preferences -`model` and `logic` must not know JabRefPreferences. See `ProxyPreferences` for encapsulated preferences and for a detailed discussion. +`model` and `logic` must not know JabRefPreferences. See `ProxyPreferences` for encapsulated preferences and [https://github.com/JabRef/jabref/pull/658](https://github.com/JabRef/jabref/pull/658) for a detailed discussion. -See (via ) for the current way how to deal with preferences. +See [https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java](https://github.com/JabRef/jabref/blob/master/src/main/java/org/jabref/logic/preferences/TimestampPreferences.java) \(via [https://github.com/JabRef/jabref/pull/3092](https://github.com/JabRef/jabref/pull/3092)\) for the current way how to deal with preferences. Defaults should go into the model package. See [Comments in this Commit](https://github.com/JabRef/jabref/commit/2f553e6557bddf7753b618b0f4edcaa6e873f719#commitcomment-15779484) ## Test Cases Imagine you want to test the method `format(String value)` in the class `BracesFormatter` which removes double braces in a given string. -- *Placing:* all tests should be placed in a class named `classTest`, e.g. `BracesFormatterTest`. -- *Naming:* the name should be descriptive enough to describe the whole test. Use the format `methodUnderTest_ expectedBehavior_context` (without the dashes). So for example `formatRemovesDoubleBracesAtBeginning`. Try to avoid naming the tests with a `test` prefix since this information is already contained in the class name. Moreover, starting the name with `test` leads often to inferior test names (see also the [Stackoverflow discussion about naming](http://stackoverflow.com/questions/155436/unit-test-naming-best-practices)). -- *Test only one thing per test:* tests should be short and test only one small part of the method. So instead of -```java -testFormat() { + +* _Placing:_ all tests should be placed in a class named `classTest`, e.g. `BracesFormatterTest`. +* _Naming:_ the name should be descriptive enough to describe the whole test. Use the format `methodUnderTest_ expectedBehavior_context` \(without the dashes\). So for example `formatRemovesDoubleBracesAtBeginning`. Try to avoid naming the tests with a `test` prefix since this information is already contained in the class name. Moreover, starting the name with `test` leads often to inferior test names \(see also the [Stackoverflow discussion about naming](http://stackoverflow.com/questions/155436/unit-test-naming-best-practices)\). +* _Test only one thing per test:_ tests should be short and test only one small part of the method. So instead of + + ```java + testFormat() { assertEqual("test", format("test")); assertEqual("{test", format("{test")); assertEqual("test", format("{{test")); assertEqual("test", format("test}}")); assertEqual("test", format("{{test}}")); -} -``` -we would have five tests containing a single `assert` statement and named accordingly (`formatDoesNotChangeStringWithoutBraces`, `formatDoesNotRemoveSingleBrace`, `formatRemovesDoubleBracesAtBeginning`, etc.). See [JUnit AntiPattern](http://www.exubero.com/junit/antipatterns.html#Multiple_Assertions) for background. -- Do *not just test happy paths*, but also wrong/weird input. -- It is recommend to write tests *before* you actually implement the functionality (test driven development). -- *Bug fixing:* write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear. -- Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method. + } + ``` + + we would have five tests containing a single `assert` statement and named accordingly \(`formatDoesNotChangeStringWithoutBraces`, `formatDoesNotRemoveSingleBrace`, `formatRemovesDoubleBracesAtBeginning`, etc.\). See [JUnit AntiPattern](http://www.exubero.com/junit/antipatterns.html#Multiple_Assertions) for background. + +* Do _not just test happy paths_, but also wrong/weird input. +* It is recommend to write tests _before_ you actually implement the functionality \(test driven development\). +* _Bug fixing:_ write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear. +* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method. ### Lists in tests * Use `assertEquals(Collections.emptyList(), actualList);` instead of `assertEquals(0, actualList.size());` to test whether a list is empty. * Similarly, use `assertEquals(Arrays.asList("a", "b"), actualList);` to compare lists instead of -```java + + ```java assertEquals(2, actualList.size()); assertEquals("a", actualList.get(0)); assertEquals("b", actualList.get(1)); -``` + ``` ### BibEntries in tests @@ -258,23 +258,27 @@ we would have five tests containing a single `assert` statement and named accord ### Files and folders in tests * If you need a temporary file in tests, then add the following Annotation before the class: -``` java -@ExtendWith(TempDirectory.class) -class TestClass{ + + ```java + @ExtendWith(TempDirectory.class) + class TestClass{ @BeforeEach void setUp(@TempDirectory.TempDir Path temporaryFolder){ } -} -``` -to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details. + } + ``` + + to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details. ### Loading Files from Resources Sometimes it is necessary to load a specific resource or to access the resource directory -```` java + +```java Path resourceDir = Paths.get(MSBibExportFormatTestFiles.class.getResource("MsBibExportFormatTest1.bib").toURI()).getParent(); -```` +``` + When the directory is needed, it is important to first point to an actual existing file. Otherwise the wrong directory will be returned. ### Preferences in tests @@ -307,27 +311,27 @@ Global variables should be avoided. Try to pass them as dependency. ### keywords sync -Database.addDatabaseChangeListener does not work as the DatabaseChangedEvent does not provide the field information. Therefore, we have to use BibtexEntry.addPropertyChangeListener(VetoableChangeListener listener) +Database.addDatabaseChangeListener does not work as the DatabaseChangedEvent does not provide the field information. Therefore, we have to use BibtexEntry.addPropertyChangeListener\(VetoableChangeListener listener\) ## Working with BibTeX data ### Working with authors -You can normalize the authors using `org.jabref.model.entry.AuthorList.fixAuthor_firstNameFirst(String)`. Then the authors always look nice. The only alternative containing all data of the names is `org.jabref.model.entry.AuthorList.fixAuthor_lastNameFirst(String)`. The other `fix...` methods omit data (like the von parts or the junior information). +You can normalize the authors using `org.jabref.model.entry.AuthorList.fixAuthor_firstNameFirst(String)`. Then the authors always look nice. The only alternative containing all data of the names is `org.jabref.model.entry.AuthorList.fixAuthor_lastNameFirst(String)`. The other `fix...` methods omit data \(like the von parts or the junior information\). ## Benchmarks -- Benchmarks can be executed by running the `jmh` gradle task (this functionality uses the [JMH Gradle plugin]( https://github.com/melix/jmh-gradle-plugin)) -- Best practices: - - Read test input from `@State` objects - - Return result of calculations (either explicitly or via a `BlackHole` object) -- [List of examples](https://github.com/melix/jmh-gradle-example/tree/master/src/jmh/java/org/openjdk/jmh/samples) +* Benchmarks can be executed by running the `jmh` gradle task \(this functionality uses the [JMH Gradle plugin](https://github.com/melix/jmh-gradle-plugin)\) +* Best practices: + * Read test input from `@State` objects + * Return result of calculations \(either explicitly or via a `BlackHole` object\) +* [List of examples](https://github.com/melix/jmh-gradle-example/tree/master/src/jmh/java/org/openjdk/jmh/samples) ## equals When creating an `equals` method follow: -1. Use the `== ` operator to check if the argument is a reference to this object. If so, return `true`. +1. Use the `==` operator to check if the argument is a reference to this object. If so, return `true`. 2. Use the `instanceof` operator to check if the argument has the correct type. If not, return `false`. 3. Cast the argument to the correct type. 4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return `true` otherwise, return `false`. @@ -335,36 +339,33 @@ When creating an `equals` method follow: Also, note: -* Always override `hashCode` when you override equals (`hashCode` also has very strict rules) (Item 9 of[Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/)) +* Always override `hashCode` when you override equals \(`hashCode` also has very strict rules\) \(Item 9 of[Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/)\) * Don’t try to be too clever * Don’t substitute another type for `Object` in the equals declaration ## Files and Paths -Always try to use the methods from the nio-package. For interoperability, they provide methods to convert between file and path. -https://docs.oracle.com/javase/tutorial/essential/io/path.html -Mapping between old methods and new methods -https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping + +Always try to use the methods from the nio-package. For interoperability, they provide methods to convert between file and path. [https://docs.oracle.com/javase/tutorial/essential/io/path.html](https://docs.oracle.com/javase/tutorial/essential/io/path.html) Mapping between old methods and new methods [https://docs.oracle.com/javase/tutorial/essential/io/legacy.html\#mapping](https://docs.oracle.com/javase/tutorial/essential/io/legacy.html#mapping) ## JavaFX The following expressions can be used in FXML attributes, according to the [official documentation](https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html#attributes) -Type | Expression | Value point to | Remark ---- | --- | --- | --- -Location | `@image.png` | path relative to the current FXML file | -Resource | `%textToBeTranslated` | key in ResourceBundle | -Attribute variable | `$idOfControl` or `$variable` | named control or variable in controller (may be path in the namespace) | resolved only once at load time -Expression binding | `${expression}` | expression, for example `textField.text` | changes to source are propagated -Bidirectional expression binding | `#{expression}` | expression | changes are propagated in both directions (not yet implemented in JavaFX, see [feature request](https://bugs.openjdk.java.net/browse/JDK-8090665)) -Event handler | `#nameOfEventHandler` | name of the event handler method in the controller | -Constant | `` | constant (here `MYSTRING` in the `Strings` class) | +| Type | Expression | Value point to | Remark | +| :--- | :--- | :--- | :--- | +| Location | `@image.png` | path relative to the current FXML file | | +| Resource | `%textToBeTranslated` | key in ResourceBundle | | +| Attribute variable | `$idOfControl` or `$variable` | named control or variable in controller \(may be path in the namespace\) | resolved only once at load time | +| Expression binding | `${expression}` | expression, for example `textField.text` | changes to source are propagated | +| Bidirectional expression binding | `#{expression}` | expression | changes are propagated in both directions \(not yet implemented in JavaFX, see [feature request](https://bugs.openjdk.java.net/browse/JDK-8090665)\) | +| Event handler | `#nameOfEventHandler` | name of the event handler method in the controller | | +| Constant | `` | constant \(here `MYSTRING` in the `Strings` class\) | | ### JavaFX Radio Buttons example -All radio buttons that should be grouped together need to have a ToggleGroup defined in the FXML code -Example: +All radio buttons that should be grouped together need to have a ToggleGroup defined in the FXML code Example: -```xml +```markup @@ -379,3 +380,4 @@ Example: ``` + diff --git a/docs/code-quality.md b/docs/code-quality.md index 717d9caf039..23f38567b42 100644 --- a/docs/code-quality.md +++ b/docs/code-quality.md @@ -2,11 +2,19 @@ We monitor the general source code quality at three places: -* [codacy](https://www.codacy.com/) is a hosted service to monitor code quality. It thereby combines the results of available open source code quality checkers such as [Checkstyle](https://checkstyle.sourceforge.io/) or [PMD](https://pmd.github.io/). The code quality analysis for JabRef is available at , especially the [list of open issues](https://app.codacy.com/gh/JabRef/jabref/issues/index). In case a rule feels wrong, it is most likely a PMD rule. The JabRef team can change the configuration at . -* [codecov](https://codecov.io/) is a solution to check code coverage of test cases. The code coverage metrics for JabRef are available at . -* [Teamscale](https://www.cqse.eu/de/produkte/teamscale/landing/) is a popular German product analyzing code quality. The analysis results are available at . +* [codacy](https://www.codacy.com/) is a hosted service to monitor code quality. It thereby combines the results of available open source code quality checkers such as [Checkstyle](https://checkstyle.sourceforge.io/) or [PMD](https://pmd.github.io/). The code quality analysis for JabRef is available at [https://app.codacy.com/gh/JabRef/jabref/dashboard](https://app.codacy.com/gh/JabRef/jabref/dashboard), especially the [list of open issues](https://app.codacy.com/gh/JabRef/jabref/issues/index). In case a rule feels wrong, it is most likely a PMD rule. The JabRef team can change the configuration at [https://app.codacy.com/p/306789/patterns/list?engine=9ed24812-b6ee-4a58-9004-0ed183c45b8f](https://app.codacy.com/p/306789/patterns/list?engine=9ed24812-b6ee-4a58-9004-0ed183c45b8f). +* [codecov](https://codecov.io/) is a solution to check code coverage of test cases. The code coverage metrics for JabRef are available at [https://codecov.io/github/JabRef/jabref](https://codecov.io/github/JabRef/jabref). +* [Teamscale](https://www.cqse.eu/de/produkte/teamscale/landing/) is a popular German product analyzing code quality. The analysis results are available at [https://demo.teamscale.com/findings.html\#/jabref/?](https://demo.teamscale.com/findings.html#/jabref/?). We strongly recommend to read following two books on code quality: -* [Java by Comparison](java.by-comparison.com/) is a book by three JabRef developers which focuses on code improvements close to single statements. It is fast to read and one gains much information from each recommendation discussed in the book. +* [Java by Comparison](https://github.com/JabRef/jabref/tree/c81740b3818c7f9311a6d7ff063243e672c821b4/docs/java.by-comparison.com) is a book by three JabRef developers which focuses on code improvements close to single statements. It is fast to read and one gains much information from each recommendation discussed in the book. * [Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/) is the standard book for advanced Java programming. Did you know that `enum` is the [recommended way to enforce a singleton instance of a class](https://learning.oreilly.com/library/view/effective-java-3rd/9780134686097/ch2.xhtml#lev3)? Did you know that one should [refer to objects by their interfaces](https://learning.oreilly.com/library/view/effective-java-3rd/9780134686097/ch9.xhtml#lev64)? + +This is how we ensure code quality in JabRef: + +* we review each external pull request by at least two [JabRef Core Developers](https://github.com/JabRef/jabref/blob/master/DEVELOPERS). +* we document our design decisions using the lightweight architectural decision records [MADR](https://adr.github.io/madr/). +* we follow the principles of [Java by Comparison](https://java.by-comparison.com/). +* we follow the principles of [Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/). +* we use [Design Pattners](https://java-design-patterns.com/patterns/) when applicable. diff --git a/docs/custom-svg-icons.md b/docs/custom-svg-icons.md index e2396929c1f..90d0b3214bc 100644 --- a/docs/custom-svg-icons.md +++ b/docs/custom-svg-icons.md @@ -4,7 +4,7 @@ JabRef uses [Material Design Icons](https://materialdesignicons.com/) for most b ![toolbar](http://i.imgur.com/KlyYrNn.png) -This tutorial aims to describe the process of adding missing icons created in a vector drawing tool like Adobe Illustrator and packing them into a *true type font* (TTF) to fit seamlessly into the JabRef framework. Already existing custom icons will be published (link is coming soon) as they need to be repacked as well. +This tutorial aims to describe the process of adding missing icons created in a vector drawing tool like Adobe Illustrator and packing them into a _true type font_ \(TTF\) to fit seamlessly into the JabRef framework. Already existing custom icons will be published \(link is coming soon\) as they need to be repacked as well. The process consists of 5 steps: @@ -20,7 +20,7 @@ Good icon design requires years of experience and cannot be covered here. Adapti ## Step 2. Packing the icons into a font -Use the [IcoMoon](https://icomoon.io) tool for packing the icons. Create a new set and import *all* icons. Rearrange them so that they have the same order as in `org.jabref.gui.JabRefMaterialDesignIcon`. This will avoid that you have to change the code points for the existing glyphs. In the settings for your icon set, set the *Grid* to 24. This is important to get the correct spacing. The name of the font is `JabRefMaterialDesign`. When your icon-set is ready, select all of them and download the font-package. +Use the [IcoMoon](https://icomoon.io) tool for packing the icons. Create a new set and import _all_ icons. Rearrange them so that they have the same order as in `org.jabref.gui.JabRefMaterialDesignIcon`. This will avoid that you have to change the code points for the existing glyphs. In the settings for your icon set, set the _Grid_ to 24. This is important to get the correct spacing. The name of the font is `JabRefMaterialDesign`. When your icon-set is ready, select all of them and download the font-package. ## Step 3. Replace the existing `JabRefMaterialDesign.ttf` @@ -28,7 +28,7 @@ Unpack the downloaded font-package and copy the `.ttf` file under `fonts` to `sr ## Step 4. Adapt the class `org.jabref.gui.JabRefMaterialDesignIcon` -Inside the font-package will be a CSS file that specifies which icon (glyph) is at which code point. If you have ordered them correctly, you newly designed icon(s) will be at the end and you can simply append them to `org.jabref.gui.JabRefMaterialDesignIcon`: +Inside the font-package will be a CSS file that specifies which icon \(glyph\) is at which code point. If you have ordered them correctly, you newly designed icon\(s\) will be at the end and you can simply append them to `org.jabref.gui.JabRefMaterialDesignIcon`: ```java TEX_STUDIO("\ue900"), @@ -43,8 +43,9 @@ Inside the font-package will be a CSS file that specifies which icon (glyph) is ## Step 5. Adapt the class `org.jabref.gui.IconTheme` -If you added an icon that already existed (but not as flat Material Design Icon), then you need to change the appropriate line in `org.jabref.gui.IconTheme`, where the icon is assigned. If you created a new one, then you need to add a line. You can specify the icon like this: +If you added an icon that already existed \(but not as flat Material Design Icon\), then you need to change the appropriate line in `org.jabref.gui.IconTheme`, where the icon is assigned. If you created a new one, then you need to add a line. You can specify the icon like this: ```java APPLICATION_EMACS(JabRefMaterialDesignIcon.EMACS) ``` + diff --git a/docs/debugging-jpackage-installations.md b/docs/debugging-jpackage-installations.md deleted file mode 100644 index b902346d0cc..00000000000 --- a/docs/debugging-jpackage-installations.md +++ /dev/null @@ -1,17 +0,0 @@ -# Debugging jpackage installations - -Sometimes issuses with modularity only arise in the installed version and do not occur if you run from source. -Using remote debugging, it's still possible to hook your IDE into the running JabRef application to enable debugging. - -## Debugging on Windows - -1. Open `build.gradle`, under jlink options remove`'--strip-debug',` -2. Build or let the CI build a new version -3. Download the modified version or portable version go to `\JabRef\runtime\bin\Jabref.bat` -4. Modify the bat file, replace the last line with -```cmd -pushd %DIR% & %JAVA_EXEC% -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -p "%~dp0/../app" -m org.jabref/org.jabref.JabRefLauncher %* & popd -``` -5. Open your IDE and add a Remote Debugging Configuration for `localhost:8000 ` -6. Start JabRef from the bat file -7. Connect with your IDE using remote debugging diff --git a/docs/development-strategy.md b/docs/development-strategy.md index 083e146728a..8571ae2f406 100644 --- a/docs/development-strategy.md +++ b/docs/development-strategy.md @@ -1,32 +1,30 @@ -# JabRef's development strategy +# JabRef's Development Strategy We aim to keep up to high-quality code standards and use code quality tools wherever possible. To ensure high code-quality, -- we follow the priniciples of [Java by Comparison](https://java.by-comparison.com/). -- we follow the principles of [Effetcive Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/). -- we use [Design Pattners](https://java-design-patterns.com/patterns/) when applicable. -- we document our design decisions using the lightweight architectural decision records [MADR](https://adr.github.io/madr/). -- we review each external pull request by at least two [JabRef Core Developers](https://github.com/JabRef/jabref/blob/master/DEVELOPERS). +* we follow the principles of [Java by Comparison](https://java.by-comparison.com/). +* we follow the principles of [Effective Java](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/). +* we use [Design Patterns](https://java-design-patterns.com/patterns/) when applicable. +* we document our design decisions using the lightweight architectural decision records [MADR](https://adr.github.io/madr/). +* we review each external pull request by at least two [JabRef Core Developers](https://github.com/JabRef/jabref/blob/master/DEVELOPERS). -## Continuos integration +Read on about our automated quality checks at [Code Quality](code-quality.md). -Since end of 2019, we just use GitHub actions to execute our tests and to creates binaries. -The binaries are create using [gradle](https://gradle.org/) and are uploaded to to . -These binaries are created without any checks to have them available as quickly as possible, even if the localization or some fetchers are broken. Deep link: . +## Continuous integration + +Since end of 2019, we just use GitHub actions to execute our tests and to creates binaries. The binaries are create using [gradle](https://gradle.org/) and are uploaded to to [https://builds.jabref.org](https://builds.jabref.org). These binaries are created without any checks to have them available as quickly as possible, even if the localization or some fetchers are broken. Deep link: [https://github.com/JabRef/jabref/actions?workflow=Deployment](https://github.com/JabRef/jabref/actions?workflow=Deployment). ## Branches The branch [master](https://builds.jabref.org/master/) is the main development line and is intended to incorporate fixes and improvements as soon as possible and to move JabRef forward to modern technologies such as the latest Java version. -Other branches are used for discussing improvements with the help of [pull requests](https://github.com/JabRef/jabref/pulls). -One can see the binaries of each branch at . -Releases mark milestones and are based on the master at a point in time. +Other branches are used for discussing improvements with the help of [pull requests](https://github.com/JabRef/jabref/pulls). One can see the binaries of each branch at [https://builds.jabref.org/](https://builds.jabref.org/). Releases mark milestones and are based on the master at a point in time. -## How JabRef aquires contributors +## How JabRef acquires contributors -* We participate in [Hacktoberfest](https://hacktoberfest.digitalocean.com/). See for details. +* We participate in [Hacktoberfest](https://hacktoberfest.digitalocean.com/). See [https://www.jabref.org/hacktoberfest/](https://www.jabref.org/hacktoberfest/) for details. * We participate in [Google Summer of Code](https://developers.google.com/open-source/gsoc/). ## Historical notes @@ -39,15 +37,13 @@ The main roadmap for JabRef 4.x was to modernize the UI, make the installation e JabRef at the beginning of 2016 had a few issues: -- Most of the code is untested, non-documented, and contains a lot of bugs and issues. -- During the lifetime of JabRef, a lot of features, UI elements and preferences have been added. All of them are loosely wired together in the UI, but the UI lacks consistency and structure. -- This makes working on JabRef interesting as in every part of the program, one can improve something. :smiley: +* Most of the code is untested, non-documented, and contains a lot of bugs and issues. +* During the lifetime of JabRef, a lot of features, UI elements and preferences have been added. All of them are loosely wired together in the UI, but the UI lacks consistency and structure. +* This makes working on JabRef interesting as in every part of the program, one can improve something. :smiley: -JabRef 3.x is the effort to try to fix a lot of these issues. -Much has been achieved, but much is still open. +JabRef 3.x is the effort to try to fix a lot of these issues. Much has been achieved, but much is still open. -We currently use two approaches: -a) rewrite and put under test to improve quality and fix bugs, -b) increase code quality. This leads to pull requests being reviewed by two JabRef developers to ensure i) code quality, ii) fit within the JabRef architecture, iii) high test coverage. +We currently use two approaches: a\) rewrite and put under test to improve quality and fix bugs, b\) increase code quality. This leads to pull requests being reviewed by two JabRef developers to ensure i\) code quality, ii\) fit within the JabRef architecture, iii\) high test coverage. Code quality includes using latest Java8 features, but also readability. + diff --git a/docs/guidelines-for-setting-up-a-local-workspace.md b/docs/guidelines-for-setting-up-a-local-workspace.md index cd4ed16b91b..de776fd9ab8 100644 --- a/docs/guidelines-for-setting-up-a-local-workspace.md +++ b/docs/guidelines-for-setting-up-a-local-workspace.md @@ -4,15 +4,13 @@ This guide explains how to set up your environment for development of JabRef. It For a complete step-by-step guide for Linux using IntellJ IDEA as the IDE, have a look at the following video instructions: - -JabRef development video tutorial - + [![](https://img.youtube.com/vi/JkFVJ6p0urw/mqdefault.jpg)](https://youtu.be/JkFVJ6p0urw) ## Prerequisites ### Java Development Kit 13 -A working Java 13 installation is required. In the command line (terminal in Linux, cmd in Windows) run `javac -version` and make sure that the reported version is Java 13 (e.g `javac 13.0.1`). If `javac` is not found or a wrong version is reported, check your PATH environment variable, your JAVA_HOME environment variable or install the most recent JDK. +A working Java 13 installation is required. In the command line \(terminal in Linux, cmd in Windows\) run `javac -version` and make sure that the reported version is Java 13 \(e.g `javac 13.0.1`\). If `javac` is not found or a wrong version is reported, check your PATH environment variable, your JAVA\_HOME environment variable or install the most recent JDK. ### git @@ -25,23 +23,23 @@ If you do not yet have a GitHub account, please [create one](https://github.com/ ### IDE -We suggest [IntelliJ IDEA](https://www.jetbrains.com/idea/) or [Eclipse](https://eclipse.org/) (`2019-12` or newer). +We suggest [IntelliJ IDEA](https://www.jetbrains.com/idea/) or [Eclipse](https://eclipse.org/) \(`2019-12` or newer\). -Under Ubuntu Linux, you can follow the [documentation from the Ubuntu Community](https://help.ubuntu.com/community/EclipseIDE#Download_Eclipse) or the [step-by-step guideline from Krizna](www.krizna.com/ubuntu/install-eclipse-in-ubuntu-12-04/) to install Eclipse. Under Windows, download it from [www.eclipse.org](http://www.eclipse.org/downloads/) and run the installer. +Under Ubuntu Linux, you can follow the [documentation from the Ubuntu Community](https://help.ubuntu.com/community/EclipseIDE#Download_Eclipse) or the [step-by-step guideline from Krizna](https://github.com/JabRef/jabref/tree/be9c788de804c2bd9e3abaf76b082b6b2e82e66f/docs/www.krizna.com/ubuntu/install-eclipse-in-ubuntu-12-04/README.md) to install Eclipse. Under Windows, download it from [www.eclipse.org](http://www.eclipse.org/downloads/) and run the installer. ## Get the code ### Fork JabRef into your GitHub account 1. Log into your GitHub account -2. Go to +2. Go to [https://github.com/JabRef/jabref](https://github.com/JabRef/jabref) 3. Create a fork by clicking at fork button on the right top corner -4. A fork repository will be created under your account (https://github.com/YOUR_USERNAME/jabref) +4. A fork repository will be created under your account \([https://github.com/YOUR\_USERNAME/jabref](https://github.com/YOUR_USERNAME/jabref)\) ### Clone your forked repository on your local machine -* In a command line, navigate to the folder where you want to place the source code (parent folder of `jabref/`). To prevent issues along the way, it is strongly recommend to choose a path that does not contain any special (non-ASCII or whitespace) characters. -* Run `git clone --depth=10 https://github.com/YOUR_USERNAME/jabref.git`. The `--depth--10` is used to limit the download to ~20 MB instead of downloading the complete history (~800 MB). If you want to dig in our commit history, feel free to download everything. +* In a command line, navigate to the folder where you want to place the source code \(parent folder of `jabref/`\). To prevent issues along the way, it is strongly recommend to choose a path that does not contain any special \(non-ASCII or whitespace\) characters. +* Run `git clone --depth=10 https://github.com/YOUR_USERNAME/jabref.git`. The `--depth--10` is used to limit the download to ~20 MB instead of downloading the complete history \(~800 MB\). If you want to dig in our commit history, feel free to download everything. * Go to the newly created jabref folder: `cd jabref` * Generate additional source code: `./gradlew assemble` * Start JabRef: `./gradlew run` @@ -51,80 +49,80 @@ Under Ubuntu Linux, you can follow the [documentation from the Ubuntu Community] ### Setup for IntelliJ IDEA -IntelliJ IDEA fully supports Gradle as a build tool, but also has an internal -build system which is usually faster. For JabRef, Gradle is required to make a -full build but once set up, IntelliJ IDEA's internal system can be used for sub-sequent -builds. +IntelliJ IDEA fully supports Gradle as a build tool, but also has an internal build system which is usually faster. For JabRef, Gradle is required to make a full build but once set up, IntelliJ IDEA's internal system can be used for sub-sequent builds. -To configure IntelliJ IDEA for developing JabRef, you should first ensure that -you have enabled both bundled plugins *Gradle* and *Gradle Extension* +To configure IntelliJ IDEA for developing JabRef, you should first ensure that you have enabled both bundled plugins _Gradle_ and _Gradle Extension_ -* Navigate to **File | Settings | Plugins | Installed** and check that you have -the *Gradle* and *Gradle Extension* enabled. +* Navigate to **File \| Settings \| Plugins \| Installed** and check that you have -After that, you can open `jabref/build.gradle` as a project. -It is crucial that Java 13 is used consistently for the JabRef project which -includes ensuring the right settings for your project structure, Gradle build, -and run configurations. + the _Gradle_ and _Gradle Extension_ enabled. + +After that, you can open `jabref/build.gradle` as a project. It is crucial that Java 13 is used consistently for the JabRef project which includes ensuring the right settings for your project structure, Gradle build, and run configurations. * Ensure you have a Java 13 SDK configured by navigating to -**File | Project Structure | Platform Settings | SDKs**. If you don't have one, add a new Java JDK and point it to the -location of a JDK 13. -* Navigate to **File | Project Structure | Project** and ensure that the projects' SDK is Java 13 -* Navigate to **File | Settings | Build, Execution, Deployment | Build Tools | Gradle** and select the Java 13 SDK as -the Gradle JVM at the bottom. + + **File \| Project Structure \| Platform Settings \| SDKs**. If you don't have one, add a new Java JDK and point it to the + + location of a JDK 13. + +* Navigate to **File \| Project Structure \| Project** and ensure that the projects' SDK is Java 13 +* Navigate to **File \| Settings \| Build, Execution, Deployment \| Build Tools \| Gradle** and select the Java 13 SDK as + + the Gradle JVM at the bottom. To prepare IntelliJ's build system two additional steps are required -* Navigate to **File | Settings | Build, Execution, Deployment | Compiler | Java Compiler**, and under -"Override compiler parameters per-module" add ([+]) the following compiler arguments for the `JabRef.main` module: - ``` +* Navigate to **File \| Settings \| Build, Execution, Deployment \| Compiler \| Java Compiler**, and under + + "Override compiler parameters per-module" add \(\[+\]\) the following compiler arguments for the `JabRef.main` module: + + ```text --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref --add-exports=org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref - ``` + ``` + * Enable annotation processors by navigating to -**File | Settings | Build, Execution, Deployment | Compiler | Annotation processors** and check "Enable annotation processing" + + **File \| Settings \| Build, Execution, Deployment \| Compiler \| Annotation processors** and check "Enable annotation processing" #### Using Gradle from within IntelliJ IDEA -Ensuring JabRef builds with Gradle should always the first step because, e.g. it generates additional sources that are -required for compiling the code. -After adjusting all settings mentioned earlier, your first step should be to +Ensuring JabRef builds with Gradle should always the first step because, e.g. it generates additional sources that are required for compiling the code. After adjusting all settings mentioned earlier, your first step should be to * Open the Gradle Tool Window with the small button that can usually be found on the right side of IDEA or navigate to -**View | Tool Windows | Gradle**. + + **View \| Tool Windows \| Gradle**. + * In the Gradle Tool Window, press the "Reimport All Gradle Projects" button to ensure that all settings are up-to-date -with the setting changes. -After that, you can use the Gradle Tool Window to build all parts JabRef and run it. To do so, expand the JabRef -project in the Gradle Tool Window and navigate to Tasks. From there, you can + with the setting changes. + +After that, you can use the Gradle Tool Window to build all parts JabRef and run it. To do so, expand the JabRef project in the Gradle Tool Window and navigate to Tasks. From there, you can + +* Build and run JabRef by double-clicking **JabRef \| Tasks \| application \| run**. -* Build and run JabRef by double-clicking **JabRef | Tasks | application | run**. -After that a new entry called "jabref [run]" will appear in the run configurations. -* Now you can also select "jabref [run]" and either run or debug the application from within IntelliJ. + After that a new entry called "jabref \[run\]" will appear in the run configurations. -You can run any other development task in a similar way. -Equivalently, this can also be executed from the terminal by running `./gradlew run`. +* Now you can also select "jabref \[run\]" and either run or debug the application from within IntelliJ. + +You can run any other development task in a similar way. Equivalently, this can also be executed from the terminal by running `./gradlew run`. #### Using IntelliJ's internal build system -You can use IntelliJ IDEA's internal build system for compiling and running JabRef during development which is -usually more responsive. However, **it's important** that you understand that JabRef relies on generated sources -which are only build through Gradle. Therefore, to build or update these dependencies you need to run the `assemble` -Gradle task at least once. +You should use IntelliJ IDEA's internal build system for compiling and running JabRef during development, because it is usually more responsive. Thereby, **it's important** that you understand that JabRef relies on generated sources which are only build through Gradle. Therefore, to build or update these dependencies you need to run the `assemble` Gradle task at least once. + +To use IntelliJ IDEA's internal build system when you build JabRef through **Build \| Build Project** or use the provided "JabRef Main" run configuration, ensure that -To use IntelliJ IDEA's internal build system when you build JabRef through **Build | Build Project** or use the provided -"JabRef Main" run configuration, ensure that +* in **File \| Settings \| Build, Execution, Deployment \| Build Tools \| Gradle** the setting "Build and run using" and -* in **File | Settings | Build, Execution, Deployment | Build Tools | Gradle** the setting "Build and run using" and -"Test using" is set to "IntelliJ IDEA". + "Test using" is set to "IntelliJ IDEA". -To use the "JabRef Main" run configuration, open **Run | Edit Configurations... | Application | JabRef Main** and +To use the "JabRef Main" run configuration, open **Run \| Edit Configurations... \| Application \| JabRef Main** and * Verify, that your JDK 13 is used * Set "VM Options" to the following: - ``` + ```text --patch-module org.jabref=build/resources/main --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref @@ -150,48 +148,45 @@ To use the "JabRef Main" run configuration, open **Run | Edit Configurations... --add-opens javafx.base/com.sun.javafx.binding=com.jfoenix --add-opens javafx.graphics/com.sun.javafx.stage=com.jfoenix --add-opens javafx.base/com.sun.javafx.event=com.jfoenix - ``` + ``` - Optionally the following entries can also be added (but they currently only produce warnings and they are not needed): + Optionally the following entries can also be added \(but they currently only produce warnings and they are not needed\): - ``` + ```text --patch-module test=fastparse_2.12-1.0.0.jar --patch-module test2=fastparse-utils_2.12-1.0.0.jar --patch-module test3=sourcecode_2.12-0.1.4.jar - ``` + ``` -Essentially, you now have the best of both worlds: You can run Gradle tasks using the Gradle Tool Window and unless you -haven't made changes to input files that generate sources, you can compile and run with IntelliJ's faster internal -build system. +Essentially, you now have the best of both worlds: You can run Gradle tasks using the Gradle Tool Window and unless you haven't made changes to input files that generate sources, you can compile and run with IntelliJ's faster internal build system. #### Using JabRef's code-style -Contributions to JabRef's source code need to have a code formatting that is consistent with existing source code. -For that purpose, JabRef provides code-style and check-style definitions. +Contributions to JabRef's source code need to have a code formatting that is consistent with existing source code. For that purpose, JabRef provides code-style and check-style definitions. * Install the [CheckStyle-IDEA plugin](http://plugins.jetbrains.com/plugin/1065?pr=idea), it can be found via the plug-in repository: - 1. Navigate to **File | Settings | Plugins | Marketplace** and search for "Checkstyle" and choose "CheckStyle-IDEA" - 2. Close the settings afterwards and restart IntelliJ -* Go to **File | Settings | Editor | Code Style** -* Click on the settings wheel (next to the scheme chooser), then click "Import Scheme" + 1. Navigate to **File \| Settings \| Plugins \| Marketplace** and search for "Checkstyle" and choose "CheckStyle-IDEA" + 2. Close the settings afterwards and restart IntelliJ +* Go to **File \| Settings \| Editor \| Code Style** +* Click on the settings wheel \(next to the scheme chooser\), then click "Import Scheme" * Select the IntelliJ configuration file `config/IntelliJ Code Style.xml`. -* Go to **File | Settings | Other Settings | Checkstyle | Configuration File** - 1. Import the CheckStyle configuration file by clicking the [+] button - 2. For the description provide e.g. "CheckStyle" - 3. Click "Browse" and choose `config/checkstyle/checkstyle.xml` - 4. Click "Next" and "Finish" - 5. Activate the CheckStyle configuration file by ticking it in the list - 6. Save settings by clicking "OK" +* Go to **File \| Settings \| Other Settings \| Checkstyle \| Configuration File** + 1. Import the CheckStyle configuration file by clicking the \[+\] button + 2. For the description provide e.g. "CheckStyle" + 3. Click "Browse" and choose `config/checkstyle/checkstyle.xml` + 4. Click "Next" and "Finish" + 5. Activate the CheckStyle configuration file by ticking it in the list + 6. Save settings by clicking "OK" #### Troubleshooting when using both IDEA and Eclipse -If you have configured Eclipse for the same project (the required steps are described below), -then the additionally added file `Log4jPlugins.java` must be excluded from the compilation process, -otherwise an error will occur during the compilation of the project: +If you have configured Eclipse for the same project \(the required steps are described below\), then the additionally added file `Log4jPlugins.java` must be excluded from the compilation process, otherwise an error will occur during the compilation of the project: -* **File | Settings | Build, Execution, Deployment | Compiler | Excludes** and add the following file to the -list ([+]), in order to exclude it: - * `src/main/java/org/jabref/gui/logging/plugins/Log4jPlugins.java` +* **File \| Settings \| Build, Execution, Deployment \| Compiler \| Excludes** and add the following file to the + + list \(\[+\]\), in order to exclude it: + + * `src/main/java/org/jabref/gui/logging/plugins/Log4jPlugins.java` ### Setup for Eclipse @@ -207,38 +202,33 @@ Make sure your Eclipse installation us up to date, Eclipse 2019-12 or newer is r 4. Open or import the existing project in Eclipse as Java project. * Remark: Importing it as gradle project will not work correctly. * Refresh the project in Eclipse -5. Create a run/debug configuration for the main class `org.jabref.JabRefLauncher` and/or for `org.jabref.JabRefMain` (both can be used equivalently) +5. Create a run/debug configuration for the main class `org.jabref.JabRefLauncher` and/or for `org.jabref.JabRefMain` \(both can be used equivalently\) * In the tab "Arguments" of the run/debug configuration, enter the following runtime VM arguments: + * Set "VM Arguments" to: - ``` - --patch-module test=fastparse_2.12-1.0.0.jar - --patch-module test2=fastparse-utils_2.12-1.0.0.jar - --patch-module test3=sourcecode_2.12-0.1.4.jar - --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref - --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref - --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls - --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls - --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls - --add-exports javafx.base/com.sun.javafx.event=org.controlsfx.controls - --add-exports javafx.base/com.sun.javafx.collections=org.controlsfx.controls - --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls - --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls - --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix - --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle - --patch-module org.jabref=build\resources\main - ``` - -6. Optional: Install the [e(fx)clipse plugin](http://www.eclipse.org/efxclipse/index.html) from the Eclipse marketplace: - 1. Help -> Eclipse Marketplace... -> Search tab - 2. Enter "e(fx)clipse" in the search dialogue - 3. Click "Go" - 4. Click "Install" button next to the plugin - 5. Click "Finish" + ```text + --patch-module test=fastparse_2.12-1.0.0.jar + --patch-module test2=fastparse-utils_2.12-1.0.0.jar + --patch-module test3=sourcecode_2.12-0.1.4.jar + --add-exports javafx.controls/com.sun.javafx.scene.control=org.jabref + --add-exports org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref + --add-exports javafx.graphics/com.sun.javafx.scene=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.event=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.collections=org.controlsfx.controls + --add-exports javafx.base/com.sun.javafx.runtime=org.controlsfx.controls + --add-exports javafx.web/com.sun.webkit=org.controlsfx.controls + --add-exports javafx.graphics/com.sun.javafx.css=org.controlsfx.controls + --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix + --add-exports com.oracle.truffle.regex/com.oracle.truffle.regex=org.graalvm.truffle + --patch-module org.jabref=build\resources\main + ``` +6. Optional: Install the [e\(fx\)clipse plugin](http://www.eclipse.org/efxclipse/index.html) from the Eclipse marketplace: 1. Help -> Eclipse Marketplace... -> Search tab 2. Enter "e\(fx\)clipse" in the search dialogue 3. Click "Go" 4. Click "Install" button next to the plugin 5. Click "Finish" 7. Now you can build and run/debug the application by either using "JabRefLauncher" or "JabRefMain". This is the recommended way, since the application starts quite fast. ## Final comments @@ -270,3 +260,4 @@ java.lang.UnsupportedClassVersionError: org/javamodularity/moduleplugin/ModuleSy ### Problems with generated source files In rare cases you might encounter problems due to out-dated automatically generated source files. Running `./gradlew clean` deletes these old copies. Do not forget to run at least `./gradlew eclipse` or `./gradlew build` afterwards to regenerate the source files. + diff --git a/docs/high-level-documentation.md b/docs/high-level-documentation.md index 88f299efa13..81117f63ed0 100644 --- a/docs/high-level-documentation.md +++ b/docs/high-level-documentation.md @@ -2,12 +2,11 @@ This page describes relevant information about the code structure of JabRef precisely and succinctly. Closer-to-code documentation is available at [Code HowTos](code-howtos.md). -We have been successfully transitioning from a spaghetti to a more structured architecture with the `model` in the center, and the `logic` as an intermediate layer towards the `gui` which is the outer shell. There are additional utility packages for `preferences` and the `cli`. The dependencies are only directed towards the center. We have JUnit tests to detect violations of the most crucial dependencies (between `logic`, `model`, and `gui`), and the build will fail automatically in these cases. +We have been successfully transitioning from a spaghetti to a more structured architecture with the `model` in the center, and the `logic` as an intermediate layer towards the `gui` which is the outer shell. There are additional utility packages for `preferences` and the `cli`. The dependencies are only directed towards the center. We have JUnit tests to detect violations of the most crucial dependencies \(between `logic`, `model`, and `gui`\), and the build will fail automatically in these cases. -The `model` represents the most important data structures (`BibDatases`, `BibEntries`, `Events`, and related aspects) and has only a little bit of logic attached. The `logic` is responsible for reading/writing/importing/exporting and manipulating the `model`, and it is structured often as an API the `gui` can call and use. Only the `gui` knows the user and his preferences and can interact with him to help him solve tasks. For each layer, we form packages according to their responsibility, i.e., vertical structuring. The `model` should have no dependencies to other classes of JabRef and the `logic` should only depend on `model` classes. The `cli` package bundles classes that are responsible for JabRef's command line interface. The `preferences` represents all information customizable by a user for her personal needs. +The `model` represents the most important data structures \(`BibDatases`, `BibEntries`, `Events`, and related aspects\) and has only a little bit of logic attached. The `logic` is responsible for reading/writing/importing/exporting and manipulating the `model`, and it is structured often as an API the `gui` can call and use. Only the `gui` knows the user and his preferences and can interact with him to help him solve tasks. For each layer, we form packages according to their responsibility, i.e., vertical structuring. The `model` should have no dependencies to other classes of JabRef and the `logic` should only depend on `model` classes. The `cli` package bundles classes that are responsible for JabRef's command line interface. The `preferences` represents all information customizable by a user for her personal needs. -We use an event bus to publish events from the `model` to the other layers. -This allows us to keep the architecture but still react upon changes within the core in the outer layers. +We use an event bus to publish events from the `model` to the other layers. This allows us to keep the architecture but still react upon changes within the core in the outer layers. You can view up-to-date diagrams for model, gui, and logic packages [at sourcespy](https://sourcespy.com/github/jabrefjabref/xx-index-.html). @@ -32,8 +31,9 @@ cli ------------> global classes cli ------------> preferences ``` -All packages and classes which are currently not part of these packages (we are still in the process of structuring) are considered as gui classes from a dependency stand of view. +All packages and classes which are currently not part of these packages \(we are still in the process of structuring\) are considered as gui classes from a dependency stand of view. ## Most Important Classes and their Relation -Both GUI and CLI are started via the `JabRefMain` which will in turn call `JabRef` which then decides whether the GUI (`JabRefFrame`) or the CLI (`JabRefCLI` and a lot of code in `JabRef`) will be started. The `JabRefFrame` represents the Window which contains a `SidePane` on the left used for the fetchers/groups Each tab is a `BasePanel` which has a `SearchBar` at the top, a `MainTable` at the center and a `PreviewPanel` or an `EntryEditor` at the bottom. Any right click on the `MainTable` is handled by the `RightClickMenu`. Each `BasePanel` holds a `BibDatabaseContext` consisting of a `BibDatabase` and the `MetaData`, which are the only relevant data of the currently shown database. A `BibDatabase` has a list of `BibEntries`. Each `BibEntry` has a key, a bibtex key and a key/value store for the fields with their values. Interpreted data (such as the type or the file field) is stored in the `TypedBibentry` type. The user can change the `JabRefPreferences` through the `PreferencesDialog`. +Both GUI and CLI are started via the `JabRefMain` which will in turn call `JabRef` which then decides whether the GUI \(`JabRefFrame`\) or the CLI \(`JabRefCLI` and a lot of code in `JabRef`\) will be started. The `JabRefFrame` represents the Window which contains a `SidePane` on the left used for the fetchers/groups Each tab is a `BasePanel` which has a `SearchBar` at the top, a `MainTable` at the center and a `PreviewPanel` or an `EntryEditor` at the bottom. Any right click on the `MainTable` is handled by the `RightClickMenu`. Each `BasePanel` holds a `BibDatabaseContext` consisting of a `BibDatabase` and the `MetaData`, which are the only relevant data of the currently shown database. A `BibDatabase` has a list of `BibEntries`. Each `BibEntry` has a key, a bibtex key and a key/value store for the fields with their values. Interpreted data \(such as the type or the file field\) is stored in the `TypedBibentry` type. The user can change the `JabRefPreferences` through the `PreferencesDialog`. + diff --git a/docs/javafx.md b/docs/javafx.md index 6d4e0f34d27..06721363efa 100644 --- a/docs/javafx.md +++ b/docs/javafx.md @@ -2,15 +2,14 @@ JabRef's recommendations about JavaFX -## Architecture: Model - View - (Controller) - ViewModel (MV(C)VM) +## Architecture: Model - View - \(Controller\) - ViewModel \(MV\(C\)VM\) -The goal of the MVVM architecture is to separate the state/behavior from the appearance of the ui. -This is archived by dividing JabRef into different layers, each having a clear responsibility. +The goal of the MVVM architecture is to separate the state/behavior from the appearance of the ui. This is archived by dividing JabRef into different layers, each having a clear responsibility. -- The _Model_ contains the business logic and data structures. These aspects are again encapsulated in the _logic_ and _model_ package, respectively. -- The _View_ controls the appearance and structure of the UI. It is usually defined in a _FXML_ file. -- _View model_ converts the data from logic and model in a form that is easily usable in the gui. Thus it controls the state of the View. Moreover, the ViewModel contains all the logic needed to change the current state of the UI or perform an action. These actions are usually passed down to the _logic_ package, after some data validation. The important aspect is that the ViewModel contains all the ui-related logic but does *not* have direct access to the controls defined in the View. Hence, the ViewModel can easily be tested by unit tests. -- The _Controller_ initializes the view model and binds it to the view. In an ideal world all the binding would already be done directly in the FXML. But JavaFX's binding expressions are not yet powerful enough to accomplish this. It is important to keep in mind that the Controller should be as minimalistic as possible. Especially one should resist the temptation to validate inputs in the controller. The ViewModel should handle data validation! It is often convenient to load the FXML file directly from the controller. +* The _Model_ contains the business logic and data structures. These aspects are again encapsulated in the _logic_ and _model_ package, respectively. +* The _View_ controls the appearance and structure of the UI. It is usually defined in a _FXML_ file. +* _View model_ converts the data from logic and model in a form that is easily usable in the gui. Thus it controls the state of the View. Moreover, the ViewModel contains all the logic needed to change the current state of the UI or perform an action. These actions are usually passed down to the _logic_ package, after some data validation. The important aspect is that the ViewModel contains all the ui-related logic but does _not_ have direct access to the controls defined in the View. Hence, the ViewModel can easily be tested by unit tests. +* The _Controller_ initializes the view model and binds it to the view. In an ideal world all the binding would already be done directly in the FXML. But JavaFX's binding expressions are not yet powerful enough to accomplish this. It is important to keep in mind that the Controller should be as minimalistic as possible. Especially one should resist the temptation to validate inputs in the controller. The ViewModel should handle data validation! It is often convenient to load the FXML file directly from the controller. The only class which access model and logic classes is the ViewModel. Controller and View have only access the ViewModel and never the backend. The ViewModel does not know the Controller or View. @@ -20,14 +19,14 @@ More details about the MVVM pattern can be found in [an article by Microsoft](ht ### ViewModel -- The ViewModel should derive from `AbstractViewModel` +* The ViewModel should derive from `AbstractViewModel` ```java public class MyDialogViewModel extends AbstractViewModel { } ``` -- Add a (readonly) property as a private field and generate the getters according to the [JavaFX bean conventions](https://docs.oracle.com/javafx/2/binding/jfxpub-binding.htm): +* Add a \(readonly\) property as a private field and generate the getters according to the [JavaFX bean conventions](https://docs.oracle.com/javafx/2/binding/jfxpub-binding.htm): ```java private final ReadOnlyStringWrapper heading = new ReadOnlyStringWrapper(); @@ -41,7 +40,7 @@ public String getHeading() { } ``` -- Create constructor which initializes the fields to their default values. Write tests to ensure that everything works as expected! +* Create constructor which initializes the fields to their default values. Write tests to ensure that everything works as expected! ```java public MyDialogViewModel(Dependency dependency) { @@ -50,7 +49,7 @@ public MyDialogViewModel(Dependency dependency) { } ``` -- Add methods which allow interaction. Again, don't forget to write tests! +* Add methods which allow interaction. Again, don't forget to write tests! ```java public void shutdown() { @@ -60,30 +59,33 @@ public void shutdown() { ### View - Controller -- The "code-behind" part of the view, which binds the `View` to the `ViewModel`. -- The usual convention is that the controller ends on the suffix `*View`. Dialogs should derive from `BaseDialog`. +* The "code-behind" part of the view, which binds the `View` to the `ViewModel`. +* The usual convention is that the controller ends on the suffix `*View`. Dialogs should derive from `BaseDialog`. ```java public class AboutDialogView extends BaseDialog ``` -- You get access to nodes in the FXML file by declaring them with the `@FXML` annotation. +* You get access to nodes in the FXML file by declaring them with the `@FXML` annotation. ```java @FXML protected Button helloButton; @FXML protected ImageView iconImage; ``` -- Dependencies can easily be injected into the controller using the `@Inject` annotation. +* Dependencies can easily be injected into the controller using the `@Inject` annotation. ```java @Inject private DialogService dialogService; ``` -- It is convenient to load the FXML-view directly from the controller class. +* It is convenient to load the FXML-view directly from the controller class. + The FXML file is loaded using `ViewLoader` based on the name of the class passed to `view`. To make this convention-over-configuration approach work, both the FXML file and the View class should have the same name and should be located in the same package. + Note that fields annotated with `@FXML` or `@Inject` only become accessible after `ViewLoader.load()` is called. -a `View` class that loads the FXML file. + + a `View` class that loads the FXML file. ```java private Dependency dependency; @@ -99,7 +101,7 @@ public AboutDialogView(Dependency dependency) { } ``` -- Dialogs should use [setResultConverter](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Dialog.html#setResultConverter-javafx.util.Callback-) to convert the data entered in the dialog to the desired result. This conversion should be done by the view model and not the controller. +* Dialogs should use [setResultConverter](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Dialog.html#setResultConverter-javafx.util.Callback-) to convert the data entered in the dialog to the desired result. This conversion should be done by the view model and not the controller. ```java setResultConverter(button -> { @@ -110,7 +112,7 @@ setResultConverter(button -> { }); ``` -- The initialize method may use data-binding to connect the ui-controls and the `ViewModel`. However, it is recommended to do as much binding as possible directly in the FXML-file. +* The initialize method may use data-binding to connect the ui-controls and the `ViewModel`. However, it is recommended to do as much binding as possible directly in the FXML-file. ```java @FXML @@ -121,7 +123,7 @@ private void initialize() { } ``` -- calling the view model: +* calling the view model: ```java @FXML @@ -136,21 +138,22 @@ The view consists a FXML file `MyDialog.fxml` which defines the structure and th ## Resources -- [curated list of awesome JavaFX frameworks, libraries, books and etc...](https://github.com/mhrimaz/AwesomeJavaFX) -- [ControlsFX](http://fxexperience.com/controlsfx/features/) amazing collection of controls -- [usage of icon fonts with JavaFX](http://aalmiray.github.io/ikonli/#_javafx) or [jIconFont](https://github.com/jIconFont/jiconfont-google_material_design_icons) or [fontawesomefx](https://bitbucket.org/Jerady/fontawesomefx/) -- [Undo manager](https://github.com/TomasMikula/UndoFX) -- [Docking manager](https://github.com/alexbodogit/AnchorFX) [or](https://github.com/RobertBColton/DockFX) -- [additional bindings](https://github.com/lestard/advanced-bindings) or [EasyBind](https://github.com/TomasMikula/EasyBind) -- [Kubed](https://github.com/hudsonb/kubed): data visualization (inspired by d3) -- [Validation framework](https://github.com/sialcasa/mvvmFX/wiki/Validation) -- [mvvm framework](https://github.com/sialcasa/mvvmFX/wiki) -- [CSS Reference](http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html) -- [JFoenix](https://github.com/jfoenixadmin/JFoenix) Material Designs look & feel -- [FontAwesomeFX](https://bitbucket.org/Jerady/fontawesomefx/overview): supports different icon fonts -- [JavaFX Documentation project](https://fxdocs.github.io/docs/index.html): Collected information on javafx in a central place -- [FXExperience](http://fxexperience.com/) JavaFX Links of the week +* [curated list of awesome JavaFX frameworks, libraries, books and etc...](https://github.com/mhrimaz/AwesomeJavaFX) +* [ControlsFX](http://fxexperience.com/controlsfx/features/) amazing collection of controls +* [usage of icon fonts with JavaFX](http://aalmiray.github.io/ikonli/#_javafx) or [jIconFont](https://github.com/jIconFont/jiconfont-google_material_design_icons) or [fontawesomefx](https://bitbucket.org/Jerady/fontawesomefx/) +* [Undo manager](https://github.com/TomasMikula/UndoFX) +* [Docking manager](https://github.com/alexbodogit/AnchorFX) [or](https://github.com/RobertBColton/DockFX) +* [additional bindings](https://github.com/lestard/advanced-bindings) or [EasyBind](https://github.com/TomasMikula/EasyBind) +* [Kubed](https://github.com/hudsonb/kubed): data visualization \(inspired by d3\) +* [Validation framework](https://github.com/sialcasa/mvvmFX/wiki/Validation) +* [mvvm framework](https://github.com/sialcasa/mvvmFX/wiki) +* [CSS Reference](http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html) +* [JFoenix](https://github.com/jfoenixadmin/JFoenix) Material Designs look & feel +* [FontAwesomeFX](https://bitbucket.org/Jerady/fontawesomefx/overview): supports different icon fonts +* [JavaFX Documentation project](https://fxdocs.github.io/docs/index.html): Collected information on javafx in a central place +* [FXExperience](http://fxexperience.com/) JavaFX Links of the week ## Features missing in JavaFX -- bidirectional binding in FXML, see [official feature request](https://bugs.openjdk.java.net/browse/JDK-8090665) +* bidirectional binding in FXML, see [official feature request](https://bugs.openjdk.java.net/browse/JDK-8090665) + diff --git a/docs/jpackage.md b/docs/jpackage.md new file mode 100644 index 00000000000..325970b58e9 --- /dev/null +++ b/docs/jpackage.md @@ -0,0 +1,39 @@ +# jpackage + +JabRef uses [jpackage](https://jdk.java.net/jpackage/) to build binary distributions for Windows, Linux, and Mac OS X. + +## Build Windows binaries locally + +Preparation: + +1. Install [WiX Toolset](https://wixtoolset.org/) + 1. Open administrative shell + 2. Use [Chocolatey](https://chocolatey.org/) to install it: `choco install wixtoolset` +2. Open [git bash](https://superuser.com/a/1053657/138868) +3. Get [JDK14](https://openjdk.java.net/projects/jdk/14/): `wget https://download.java.net/java/GA/jdk14/076bab302c7b4508975440c56f6cc26a/36/GPL/openjdk-14_windows-x64_bin.zip` +4. Extract JDK14: `unzip openjdk-14_windows-x64_bin.zip` + +Compile: + +1. `export BADASS_JLINK_JPACKAGE_HOME=jdk-14/` +2. `./gradlew -PprojVersion="5.0.50013" -PprojVersionInfo="5.0-ci.13--2020-03-05--c8e5924" jlinkZip` +3. `./gradlew -PprojVersion="5.0.50013" -PprojVersionInfo="5.0-ci.13--2020-03-05--c8e5924" jpackage` + +## Debugging jpackage installations + +Sometimes issues with modularity only arise in the installed version and do not occur if you run from source. Using remote debugging, it's still possible to hook your IDE into the running JabRef application to enable debugging. + +### Debugging on Windows + +1. Open `build.gradle`, under jlink options remove `--strip-debug` +2. Build or let the CI build a new version +3. Download the modified version or portable version go to `\JabRef\runtime\bin\Jabref.bat` +4. Modify the bat file, replace the last line with + + ```cmd + pushd %DIR% & %JAVA_EXEC% -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -p "%~dp0/../app" -m org.jabref/org.jabref.JabRefLauncher %* & popd + ``` + +5. Open your IDE and add a "Remote Debugging Configuration" for `localhost:8000` +6. Start JabRef from the bat file +7. Connect with your IDE using remote debugging diff --git a/docs/testing.md b/docs/testing.md index 8a1e08e5d7b..5f423fbe548 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -6,10 +6,9 @@ To quickly host a local PostgreSQL database, execute following statement: -```shell command -docker run -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -p 5432:5432 --name db postgres:10 postgres -c log_statement=all -``` +\`\`\`shell command docker run -d -e POSTGRES\_USER=postgres -e POSTGRES\_PASSWORD=postgres -e POSTGRES\_DB=postgres -p 5432:5432 --name db postgres:10 postgres -c log\_statement=all +```text Set the environment variable `DBMS` to `postgres` (or leave it unset) Then, all DBMS Tests (annotated with `@org.jabref.testutils.category.DatabaseTest`) run properly. @@ -23,3 +22,4 @@ docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mys ``` Set the environment variable `DBMS` to `mysql`. + diff --git a/docs/tools.md b/docs/tools.md index 94c2a57aed6..7a98ecb7b10 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -11,26 +11,25 @@ This page lists some software we consider useful. Here, we collect some helpful git hints -* https://github.com/blog/2019-how-to-undo-almost-anything-with-git -* https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md -* awesome hints and tools regarding git: https://github.com/dictcp/awesome-git +* [https://github.com/blog/2019-how-to-undo-almost-anything-with-git](https://github.com/blog/2019-how-to-undo-almost-anything-with-git) +* [https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md](https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md) +* awesome hints and tools regarding git: [https://github.com/dictcp/awesome-git](https://github.com/dictcp/awesome-git) ### Rebase everything as one commit on master -* Precondition: `JabRef/jabref` is [configured as upstream](https://help.github.com/articles/configuring-a-remote-for-a-fork/). +* Precondition: `JabRef/jabref` is [configured as upstream](https://help.github.com/articles/configuring-a-remote-for-a-fork/). +* Fetch recent commits and prune non-existing branches: `git fetch upstream --prune` +* Merge recent commits: `git merge upstream/master` +* If there are conflicts, resolve them +* Reset index to upstream/master: `git reset upstream/master` +* Review the changes and create a new commit using git gui: `git gui&` +* Do a force push: `git push -f origin` -1. Fetch recent commits and prune non-existing branches: `git fetch upstream --prune` -2. Merge recent commits: `git merge upstream/master` -3. If there are conflicts, resolve them -4. Reset index to upstream/master: `git reset upstream/master` -5. Review the changes and create a new commit using git gui: `git gui&` -6. Do a force push: `git push -f origin` - -See also: https://help.github.com/articles/syncing-a-fork/ +See also: [https://help.github.com/articles/syncing-a-fork/](https://help.github.com/articles/syncing-a-fork/) ## Tooling for Windows -(As Administrator - one time) +\(As Administrator - one time\) 1. Install [chocolatey](https://chocolatey.org/) 2. `choco install git` @@ -50,30 +49,27 @@ Then, each weak do `choco upgrade all` to ensure all tooling is uptodate. #### ConEmu plus clink -* [ConEmu] -> Preview Version - Aim: Colorful console with tabs +* [ConEmu](http://conemu.github.io/) -> Preview Version - Aim: Colorful console with tabs * At first start: - * "Choose your startup task ...": `{Bash::Git bash}} + * "Choose your startup task ...": \`{Bash::Git bash}} * `OK` - * Upper right corner: "Settings..." (third entrry Eintrag) - * Startup/Tasks: Choose task no. 7 ("Bash::Git bash"). At "Task parameters" `/dir C:\git-repositories\jabref\jabref` + * Upper right corner: "Settings..." \(third entrry Eintrag\) + * Startup/Tasks: Choose task no. 7 \("Bash::Git bash"\). At "Task parameters" `/dir C:\git-repositories\jabref\jabref` * `Save Settings` -* [clink] - Aim: Unix keys (Alt+B, Ctrl+S, etc.) also available at the prompt of `cmd.exe` +* [clink](http://mridgers.github.io/clink/) - Aim: Unix keys \(Alt+B, Ctrl+S, etc.\) also available at the prompt of `cmd.exe` #### Other bundles -* [Cmder] - bundles ConEmu plus clink +* [Cmder](http://cmder.net/) - bundles ConEmu plus clink ### Tools for working with XMP -- Validate XMP: http://www.pdflib.com/knowledge-base/xmp-metadata/free-xmp-validator/ + +* Validate XMP: [http://www.pdflib.com/knowledge-base/xmp-metadata/free-xmp-validator/](http://www.pdflib.com/knowledge-base/xmp-metadata/free-xmp-validator/) ### Some useful keyboard shortcuts * [AutoHotkey](http://autohotkey.com/) - Preparation for the next step -* - Aim: Have Win+C opening ConEmu - +* [https://github.com/koppor/autohotkey-scripts](https://github.com/koppor/autohotkey-scripts) - Aim: Have Win+C opening ConEmu 1. Clone the repository locally. - 2. Then link `ConEmu.ahk` and `WindowsExplorer.ahk` at the startup menu (Link creation works with drag'n'drop using the right mouse key and then choosing "Create link" when dropping). Hint: Startup is in the folder `Startup` (German: `Autostart`) at `%APPDATA%\Microsoft\Windows\Start Menu\Programs\` - accessible via `Win+r`: `shell:startup` + 2. Then link `ConEmu.ahk` and `WindowsExplorer.ahk` at the startup menu \(Link creation works with drag'n'drop using the right mouse key and then choosing "Create link" when dropping\). Hint: Startup is in the folder `Startup` \(German: `Autostart`\) at `%APPDATA%\Microsoft\Windows\Start Menu\Programs\` - accessible via `Win+r`: `shell:startup` - [ConEmu]: http://conemu.github.io/ - [clink]: http://mridgers.github.io/clink/ - [Cmder]: http://cmder.net/ diff --git a/docs/ui-recommendations.md b/docs/ui-recommendations.md index f14f62a9522..1fdf8b75367 100644 --- a/docs/ui-recommendations.md +++ b/docs/ui-recommendations.md @@ -1,9 +1,9 @@ # Recommendations for UI design -- [Designing More Efficient Forms: Structure, Inputs, Labels and Actions](https://uxplanet.org/designing-more-efficient-forms-structure-inputs-labels-and-actions-e3a47007114f) -- [Input form label alignment top or left?](https://ux.stackexchange.com/questions/8480/input-form-label-alignment-top-or-left) - - For a usual form, place the label above the text field - - If the user uses the form often to edit fields, then it might make sense to switch to left-aligned labels +* [Designing More Efficient Forms: Structure, Inputs, Labels and Actions](https://uxplanet.org/designing-more-efficient-forms-structure-inputs-labels-and-actions-e3a47007114f) +* [Input form label alignment top or left?](https://ux.stackexchange.com/questions/8480/input-form-label-alignment-top-or-left) + * For a usual form, place the label above the text field + * If the user uses the form often to edit fields, then it might make sense to switch to left-aligned labels ## Designing GUI Confirmation dialogs @@ -12,4 +12,5 @@ 3. Identify the item at risk 4. Name your buttons for the actions -More information: +More information: [http://ux.stackexchange.com/a/768](http://ux.stackexchange.com/a/768) + diff --git a/external-libraries.txt b/external-libraries.txt index 4548d3128a5..f67e09af7fd 100644 --- a/external-libraries.txt +++ b/external-libraries.txt @@ -1,26 +1,29 @@ +# External libraries + This document lists the fonts, icons, and libraries used by JabRef. This file is manually kept in sync with build.gradle and the binary jars contained in the lib/ directory. One can list all dependencies by using Gradle task `dependencyReport`. -It generated the file [build/reports/project/dependencies.txt](build/reports/project/dependencies.txt). +It generates the file [build/reports/project/dependencies.txt](build/reports/project/dependencies.txt). +There, [one can use](https://stackoverflow.com/a/49727249/873282) `sed 's/^.* //' | sort | uniq` to flatten the dependencies. -# Legend +## Legend -## License +### License We follow the [SPDX license identifiers](https://spdx.org/licenses/). In case you add a library, please use these identifiers. For instance, "BSD" is not exact enough, there are numerous variants out there: BSD-2-Clause, BSD-3-Clause-No-Nuclear-Warranty, ... Note that the SPDX license identifiers are different from the ones used by debian. See https://wiki.debian.org/Proposals/CopyrightFormat for more information. -# bst files +## bst files Project: IEEEtran Path: src/main/resources/bst/IEEEtran.bst URL: https://www.ctan.org/tex-archive/macros/latex/contrib/IEEEtran/bibtex License: LPPL-1.3 -# Fonts and Icons +## Fonts and Icons The loading animation during loading of recommendations from Mr. DLib is created by and is free of use under license CC0 1.0. @@ -32,29 +35,44 @@ License: SIL Open Font License, Version 1.1 Note: It is important to include v1.5.54 or later as v1.5.54 is the first version offering fixed code points. Do not confuse with http://zavoloklom.github.io/material-design-iconic-font/ -# Libraries +## Libraries (Sorted alphabetically by Id) -Id: com.airhacks:afterburner.fx -Project: afterburner.fx -URL: https://github.com/AdamBien/afterburner.fx -License: Apache-2.0 - -Id: com.apple:AppleJavaExtensions -Project: AppleJavaExtensions -URL: https://developer.apple.com/legacy/library/samplecode/AppleJavaExtensions/Introduction/Intro.html -License: Apple License - Id: com.github.tomtung Project: latex2unicode URL: https://github.com/tomtung/latex2unicode License: Apache-2.0 -Id: com.impossibl.pgjdbc-ng:pgjdbc-ng -Project: pgjdbc-ng -URL: http://impossibl.github.io/pgjdbc-ng -License: BSD-3-Clause +Id: com.google.code.gson:gson +Project: Google Guava +URL: https://github.com/google/gson +License: Apache-2.0 + +Id: com.google.guava:failureaccess +Project: Google Guava +URL: https://github.com/google/guava +License: Apache-2.0 +Note: See https://github.com/google/guava/issues/3437 for a discussion that this dependency is really required. + +Id: com.google.guava:guava +Project: Google Guava +URL: https://github.com/google/guava +License: Apache-2.0 + +Id: com.google.j2objc:j2objc-annotations +Project: j2objc-annotations +URL: https://github.com/google/j2objc +License: Apache-2.0 + +Id: com.ibm.icu:icu4j +Project: International Components for Unicode for Java (ICU4J) +URL: https://wiki.eclipse.org/ICU4J + +Id: com.jfoenix:jfoenix +Project: JavaFX MAterial Design Library +URL: https://github.com/jfoenixadmin/JFoenix +License: Apache-2.0 Id: com.konghq.unirest Project: Unirest for Java @@ -76,31 +94,46 @@ Project: Oracle's JDBC drivers URL: https://repo1.maven.org/maven2/com/oracle/ojdbc/ojdbc10/19.3.0.0/ojdbc10-19.3.0.0.pom License: Oracle Free Use Terms and Conditions (FUTC) -Id: com.sibvisions.external.jvxfx:DnDTabPane -Project: Drag'n'Drop TabPane -URL: https://github.com/sibvisions/javafx.DndTabPane -License: EPL-1.0 +Id: com.sun.istack:istack-commons-runtime +Project: iStack Common Utility Code +URL: https://github.com/eclipse-ee4j/jaxb-istack-commons +License: BSD-3-Clause (with copyright as described in Eclipse Distribution License - v 1.0 - see https://wiki.spdx.org/view/Legal_Team/License_List/Licenses_Under_Consideration for details) + +Id: com.sun.xml.fastinfoset:FastInfoset +Project: Fast Infoset +URL: https://github.com/eclipse-ee4j/jaxb-fi +License: Apache-2.0 Id: commons-cli:commons-cli Project: Apache Commons CLI URL: http://commons.apache.org/cli/ License: Apache-2.0 +Id: commons-codec:commons-codec +Project: Apache Commons Codec +URL: https://commons.apache.org/proper/commons-codec/ +License: Apache-2.0 + Id: commons-logging:commons-logging Project: Apache Commons Logging URL: http://commons.apache.org/logging/ License: Apache-2.0 -Id: de.codecentric.centerdevice -Project: javafxsvg -URL: https://github.com/codecentric/javafxsvg -License: BSD 3-Clause +Id: de.jensd:fontawesomefx-commons +Project: FontAwesomeFX Commons +URL: https://bitbucket.org/Jerady/fontawesomefx +License: Apache-2.0 Id: de.jensd:fontawesomefx-materialdesignfont -Project: FontAwesomeFX +Project: FontAwesomeFX Material Design Font URL: https://bitbucket.org/Jerady/fontawesomefx License: Apache-2.0 +Id: de.saxsys:mvvmfx +Project: mvvm(fx) +URL: https://github.com/sialcasa/mvvmFX +License: Apache-2.0 + Id: de.saxsys:mvvmfx-validation Project: mvvm(fx) URL: https://github.com/sialcasa/mvvmFX @@ -111,6 +144,11 @@ Project: Citeproc-Java URL: http://michel-kraemer.github.io/citeproc-java/ Licence: Apache-2.0 +Id: eu.lestard:doc-annotations +Project: doc annotations +URL: https://github.com/lestard/doc-annotations +License: MIT + Id: info.debatty:java-string-similarity Project: Java String Similarity URL: https://github.com/tdebatty/java-string-similarity @@ -121,17 +159,37 @@ Project: java-diff-utils URL: https://github.com/java-diff-utils/java-diff-utils License: Apache-2.0 -Id: net.java.dev.glazedlists:glazedlists_java15 -Project: Glazed Lists -URL: http://www.glazedlists.com/ -License: LGPL-2.1 (not explicitly, but no comments in the source header) and MPL-2.0 +Id: jakarta.activation:jakarata.activation-api +Project: Jakarta Activation +URL: https://eclipse-ee4j.github.io/jaf/ +License: BSD-3-Clause -Id: org.antlr:antlr +Id: jakarta.annotation:jakarata.annotation-api +Project: Jakarta Annotations +URL: https://projects.eclipse.org/projects/ee4j.ca +License: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +Id: jakarta.xml.bind:jakarta.xml.bind-api +Project: Jakarta XML Binding project +URL: https://github.com/eclipse-ee4j/jaxb-api +License: BSD-3-Clause; sometimes EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +Id: net.jcip:jcip-annotations +Project: JCIP (Java Concurrency in Practice) Annotations under Apache License +URL: http://stephenc.github.io/jcip-annotations/ +License: Apache-2.0 + +Id: net.jodah:typetools +Project: TypeTools +URL: https://github.com/jhalterman/typetools +License: Apache-2.0 + +Id: org.antlr:antlr-runtime Project: ANTLR 3 URL: http://www.antlr3.org/ License: BSD-3-Clause -Id: org.antlr:antlr4 +Id: org.antlr:antlr4-runtime Project: ANTLR 4 URL: http://www.antlr.org/ License: BSD-3-Clause @@ -146,6 +204,11 @@ Project: Apache Commons Lang URL: https://commons.apache.org/proper/commons-lang/ License: Apache-2.0 +Id: org.apache.commons:commons-text +Project: Apache Commons Text +URL: https://commons.apache.org/proper/commons-text/ +License: Apache-2.0 + Id: org.apache.logging.log4j Project: Apache Log2j 2 URL: http://logging.apache.org/log4j/2.x/ @@ -166,6 +229,11 @@ Project: Apache PDFBox URL: http://pdfbox.apache.org License: Apache-2.0 +Id: org.apache.tika:tika-core +Project: Apache Tika +URL: https://tika.apache.org/ +License: Apache-2.0 + Id: org.bouncycastle:bcprov-jdk15on Project: The Legion of the Bouncy Castle URL: https://www.bouncycastle.org/ @@ -186,7 +254,7 @@ Project: ControlsFX URL: http://fxexperience.com/controlsfx/ License: BSD-3-Clause -Id: org.fx.misc.easybind:easybind +Id: org.fxmisc.easybind:easybind Project: EasyBind URL: https://github.com/TomasMikula/EasyBind License: BSD-2-Clause @@ -211,6 +279,41 @@ Project: MariaDB Java Client URL: https://mariadb.com/kb/en/library/about-mariadb-connector-j/ License: LGPL-2.1-or-later +Id: org.openjfx:javafx-base +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + +Id: org.openjfx:javafx-controls +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + +Id: org.openjfx:javafx-fxml +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + +Id: org.openjfx:javafx-graphics +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + +Id: org.openjfx:javafx-media +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + +Id: org.openjfx:javafx-swing +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + +Id: org.openjfx:javafx-web +Project JavaFX +URL: https://openjfx.io/ +License: GPL-2.0 WITH Classpath-exception-2.0 + Id: org.openoffice:juh Project: OpenOffice.org URL: http://www.openoffice.org/api/SDK @@ -231,14 +334,97 @@ Project: OpenOffice.org URL: http://www.openoffice.org/api/SDK License: Apache-2.0 -Id: org.xmlunit:xmlunit-core -Project: XMLUnit -URL: http://www.xmlunit.org/ -License: Apache-2.0 - -Id: org.xmlunit:xmlunit-matchers -Project: XMLUnit -URL: http://www.xmlunit.org/ -License: Apache-2.0 - -The last entry has to end with an empty line. Otherwise the entry is not present in About.html. +## Sorted list of runtime dependencies output by gradle + +```text +com.github.tomtung:latex2unicode_2.12:0.2.6 +com.google.code.gson:gson:2.8.6 +com.google.errorprone:error_prone_annotations:2.3.4 +com.google.guava:failureaccess:1.0.1 +com.google.guava:guava:28.2-jre +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava +com.google.j2objc:j2objc-annotations:1.3 +com.ibm.icu:icu4j:62.1 +com.jfoenix:jfoenix:9.0.9 +com.konghq:unirest-java:3.6.00 +com.microsoft.azure:applicationinsights-core:2.4.1 +com.microsoft.azure:applicationinsights-logging-log4j2:2.4.1 +com.oracle.ojdbc:ojdbc10:19.3.0.0 +com.oracle.ojdbc:ons:19.3.0.0 +com.oracle.ojdbc:osdt_cert:19.3.0.0 +com.oracle.ojdbc:osdt_core:19.3.0.0 +com.oracle.ojdbc:simplefan:19.3.0.0 +com.oracle.ojdbc:ucp:19.3.0.0 +com.sun.istack:istack-commons-runtime:3.0.8 +com.sun.xml.fastinfoset:FastInfoset:1.2.16 +commons-cli:commons-cli:1.4 +commons-codec:commons-codec:1.11 +commons-logging:commons-logging:1.2 +de.jensd:fontawesomefx-commons:11.0 +de.jensd:fontawesomefx-materialdesignfont:1.7.22-11 +de.saxsys:mvvmfx-validation:1.9.0-SNAPSHOT +de.saxsys:mvvmfx:1.8.0 +de.undercouch:citeproc-java:2.1.0-SNAPSHOT +eu.lestard:doc-annotations:0.2 +info.debatty:java-string-similarity:1.2.1 +io.github.java-diff-utils:java-diff-utils:4.5 +jakarta.activation:jakarta.activation-api:1.2.1 +jakarta.annotation:jakarta.annotation-api:1.3.5 +jakarta.xml.bind:jakarta.xml.bind-api:2.3.2 +net.jcip:jcip-annotations:1.0 +net.jodah:typetools:0.6.1 +org.antlr:antlr-runtime:3.5.2 +org.antlr:antlr4-runtime:4.8-1 +org.apache.commons:commons-csv:1.8 +org.apache.commons:commons-lang3:3.9 +org.apache.commons:commons-text:1.8 +org.apache.httpcomponents:httpasyncclient:4.1.4 +org.apache.httpcomponents:httpclient:4.5.11 +org.apache.httpcomponents:httpcore-nio:4.4.13 +org.apache.httpcomponents:httpcore:4.4.13 +org.apache.httpcomponents:httpmime:4.5.11 +org.apache.logging.log4j:log4j-api:3.0.0-SNAPSHOT +org.apache.logging.log4j:log4j-core:3.0.0-SNAPSHOT +org.apache.logging.log4j:log4j-jcl:3.0.0-SNAPSHOT +org.apache.logging.log4j:log4j-plugins:3.0.0-SNAPSHOT +org.apache.logging.log4j:log4j-slf4j18-impl:3.0.0-SNAPSHOT +org.apache.pdfbox:fontbox:2.0.18 +org.apache.pdfbox:pdfbox:2.0.18 +org.apache.pdfbox:xmpbox:2.0.18 +org.apache.tika:tika-core:1.23 +org.bouncycastle:bcprov-jdk15on:1.64 +org.checkerframework:checker-qual:2.10.0 +org.controlsfx:controlsfx:11.0.1 +org.fxmisc.easybind:easybind:1.0.3 +org.fxmisc.flowless:flowless:0.6.1 +org.fxmisc.richtext:richtextfx:0.10.4 +org.fxmisc.undo:undofx:2.1.0 +org.fxmisc.wellbehaved:wellbehavedfx:0.3.3 +org.glassfish.hk2.external:jakarta.inject:2.6.1 +org.glassfish.jaxb:jaxb-runtime:2.3.2 +org.glassfish.jaxb:txw2:2.3.2 +org.graalvm.js:js:19.2.1 +org.graalvm.regex:regex:19.2.1 +org.graalvm.sdk:graal-sdk:19.2.1 +org.graalvm.truffle:truffle-api:19.2.1 +org.jbibtex:jbibtex:1.0.17 +org.jsoup:jsoup:1.13.1 +org.jvnet.staxex:stax-ex:1.8.1 +org.mariadb.jdbc:mariadb-java-client:2.5.4 +org.openjfx:javafx-base:13.0.2 +org.openjfx:javafx-controls:13.0.2 +org.openjfx:javafx-fxml:13.0.2 +org.openjfx:javafx-graphics:13.0.2 +org.openjfx:javafx-media:13.0.2 +org.openjfx:javafx-swing:13.0.2 +org.openjfx:javafx-web:13.0.2 +org.ow2.asm:asm-analysis:6.2.1 +org.ow2.asm:asm-commons:6.2.1 +org.ow2.asm:asm-tree:6.2.1 +org.ow2.asm:asm-util:6.2.1 +org.ow2.asm:asm:6.2.1 +org.postgresql:postgresql:42.2.10 +org.reactfx:reactfx:2.0-M5 +org.scala-lang:scala-library:2.12.8 +org.slf4j:slf4j-api:2.0.0-alpha1 +``` diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b7c8c5dbf58..a2bf1313b8a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat index 9618d8d9607..62bd9b9ccef 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" diff --git a/scripts/generate-authors.sh b/scripts/generate-authors.sh index e79a6af3383..64a56329233 100755 --- a/scripts/generate-authors.sh +++ b/scripts/generate-authors.sh @@ -57,7 +57,7 @@ cd "$(dirname "$(readlink -f "$BASH_SOURCE")")/.." # authors %aN = author name # co-authors - coauthors=$(git log --grep=Co-authored | grep "Co-au" | grep "<" | sed "s/.*Co-authored-by: \(.*\) <.*/\1/") - echo -e "$authors\n$(git log --format='%aN')\n$coauthors" | grep -v "dependabot" | grep -v "halirutan" | grep -v "matthiasgeiger" | grep -v "Gitter Badger" | LC_ALL=C.UTF-8 sort --unique --ignore-case + coauthors=$(git log -i --grep=co-authored-by | grep -i "co-authored-by" | sed "s/.*co-authored-by: \(.*\)/\1/I" | sed "s/ <.*//") + echo -e "$authors\n$(git log --format='%aN')\n$coauthors" | grep -v "@Siedlerchr" | grep -v "^Christoph$" | grep -v "oscargus" | grep -v "dependabot" | grep -v "github actions" | grep -v "github actions" | grep -v "igorsteinmacher" | grep -v "halirutan" | grep -v "matthiasgeiger" | grep -v "Gitter Badger" | LC_ALL=C.UTF-8 sort --unique --ignore-case } > AUTHORS diff --git a/snap/hooks/connect-plug-etc-chromium-native-messaging-jabref b/snap/hooks/connect-plug-etc-chromium-native-messaging-jabref new file mode 100755 index 00000000000..2644fc7026a --- /dev/null +++ b/snap/hooks/connect-plug-etc-chromium-native-messaging-jabref @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ ! -d /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts ]; then + echo "Missing directory, create it manually then try again:" + echo "sudo mkdir -p /etc/chromium/native-messaging-hosts" + exit 1 +fi + +cp "$SNAP/lib/native-messaging-host/chromium/org.jabref.jabref.json" /etc/chromium/native-messaging-hosts/org.jabref.jabref.json diff --git a/snap/hooks/connect-plug-etc-opt-chrome-native-messaging-jabref b/snap/hooks/connect-plug-etc-opt-chrome-native-messaging-jabref new file mode 100755 index 00000000000..025087770a7 --- /dev/null +++ b/snap/hooks/connect-plug-etc-opt-chrome-native-messaging-jabref @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ ! -d /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts ]; then + echo "Missing directory, create it manually then try again:" + echo "sudo mkdir -p /etc/opt/chrome/native-messaging-hosts" + exit 1 +fi + +cp "$SNAP/lib/native-messaging-host/chromium/org.jabref.jabref.json" /etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json diff --git a/snap/hooks/connect-plug-browser-extension b/snap/hooks/connect-plug-hostfs-mozilla-native-messaging-jabref similarity index 60% rename from snap/hooks/connect-plug-browser-extension rename to snap/hooks/connect-plug-hostfs-mozilla-native-messaging-jabref index df5cde565f3..ac597118d7c 100755 --- a/snap/hooks/connect-plug-browser-extension +++ b/snap/hooks/connect-plug-hostfs-mozilla-native-messaging-jabref @@ -6,4 +6,4 @@ if [ ! -d /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts ]; then exit 1 fi -cp $SNAP/lib/org.jabref.jabref.json /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json \ No newline at end of file +cp "$SNAP/lib/native-messaging-host/firefox/org.jabref.jabref.json" /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json diff --git a/snap/hooks/disconnect-plug-etc-chromium-native-messaging-jabref b/snap/hooks/disconnect-plug-etc-chromium-native-messaging-jabref new file mode 100755 index 00000000000..e68ad21ca73 --- /dev/null +++ b/snap/hooks/disconnect-plug-etc-chromium-native-messaging-jabref @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ ! -f /etc/chromium/native-messaging-hosts/org.jabref.jabref.json ]; then + exit 0 +elif grep --quiet '"path": "/snap' /etc/chromium/native-messaging-hosts/org.jabref.jabref.json; then + rm /etc/chromium/native-messaging-hosts/org.jabref.jabref.json +fi diff --git a/snap/hooks/disconnect-plug-etc-opt-chrome-native-messaging-jabref b/snap/hooks/disconnect-plug-etc-opt-chrome-native-messaging-jabref new file mode 100755 index 00000000000..50eff0b9292 --- /dev/null +++ b/snap/hooks/disconnect-plug-etc-opt-chrome-native-messaging-jabref @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ ! -f /etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json ]; then + exit 0 +elif grep --quiet '"path": "/snap' /etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json; then + rm /etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json +fi diff --git a/snap/hooks/disconnect-plug-browser-extension b/snap/hooks/disconnect-plug-hostfs-mozilla-native-messaging-jabref similarity index 99% rename from snap/hooks/disconnect-plug-browser-extension rename to snap/hooks/disconnect-plug-hostfs-mozilla-native-messaging-jabref index 5af57cf27f9..280d1afc79d 100755 --- a/snap/hooks/disconnect-plug-browser-extension +++ b/snap/hooks/disconnect-plug-hostfs-mozilla-native-messaging-jabref @@ -4,4 +4,4 @@ if [ ! -f /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabre exit 0 elif grep --quiet '"path": "/snap' /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json; then rm /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json -fi \ No newline at end of file +fi diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index ccba913feba..3734858e789 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -12,6 +12,28 @@ license: MIT architectures: - build-on: amd64 +plugs: + desktop: + desktop-legacy: + wayland: + unity7: + home: + opengl: + network-bind: + removable-media: + hostfs-mozilla-native-messaging-jabref: + interface: system-files + write: + - /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json + etc-opt-chrome-native-messaging-jabref: + interface: system-files + write: + - /etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json + etc-chromium-native-messaging-jabref: + interface: system-files + write: + - /etc/chromium/native-messaging-hosts/org.jabref.jabref.json + apps: jabref: command: bin/JabRef @@ -22,34 +44,13 @@ apps: environment: _JAVA_OPTIONS: "-Duser.home=$SNAP_USER_DATA" -plugs: - desktop: - desktop-legacy: - wayland: - unity7: - home: - opengl: - network-bind: - removable-media: - plugs: - hostfs-mozilla-native-messaging-jabref: - interface: system-files - write: - - /var/lib/snapd/hostfs/usr/lib/mozilla/native-messaging-hosts/org.jabref.jabref.json - hostfs-chrome-native-messaging-jabref: - interface: system-files - write: - - /var/lib/snapd/hostfs/etc/opt/chrome/native-messaging-hosts/org.jabref.jabref.json - hostfs-chromium-native-messaging-jabref: - interface: system-files - write: - - /var/lib/snapd/hostfs/etc/chromium/native-messaging-hosts/org.jabref.jabref.json + parts: jabref: plugin: dump - source: build/distribution/JabRef-portable_linux.tar.gz + # source: build/distribution/JabRef-5.0-portable_linux.tar.gz # Use this source for debug purposes: - # source: https://builds.jabref.org/master/JabRef-portable_linux.tar.gz + source: https://builds.jabref.org/master/JabRef-5.0-portable_linux.tar.gz stage-packages: - x11-utils override-build: | diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 6ef392d0005..91b721e8a4a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -22,7 +22,6 @@ provides com.airhacks.afterburner.views.ResourceLocator with org.jabref.gui.util.JabRefResourceLocator; - provides com.airhacks.afterburner.injection.PresenterFactory with org.jabref.gui.DefaultInjector; diff --git a/src/main/java/org/jabref/Globals.java b/src/main/java/org/jabref/Globals.java index 8126ad26074..5f8177ffbca 100644 --- a/src/main/java/org/jabref/Globals.java +++ b/src/main/java/org/jabref/Globals.java @@ -27,10 +27,13 @@ import com.google.common.base.StandardSystemProperty; import com.microsoft.applicationinsights.TelemetryClient; import com.microsoft.applicationinsights.TelemetryConfiguration; -import com.microsoft.applicationinsights.internal.shutdown.SDKShutdownActivity; import com.microsoft.applicationinsights.telemetry.SessionState; import kong.unirest.Unirest; +/** + * @deprecated try to use {@link StateManager} and {@link org.jabref.preferences.PreferencesService} + */ +@Deprecated public class Globals { /** @@ -105,18 +108,15 @@ private static void stopTelemetryClient() { getTelemetryClient().ifPresent(client -> { client.trackSessionState(SessionState.End); client.flush(); - - //FIXME: Workaround for bug https://github.com/Microsoft/ApplicationInsights-Java/issues/662 - SDKShutdownActivity.INSTANCE.stopAll(); }); } private static void startTelemetryClient() { TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.getActive(); - telemetryConfiguration.setInstrumentationKey(Globals.BUILD_INFO.getAzureInstrumentationKey()); + telemetryConfiguration.setInstrumentationKey(Globals.BUILD_INFO.azureInstrumentationKey); telemetryConfiguration.setTrackingIsDisabled(!Globals.prefs.shouldCollectTelemetry()); telemetryClient = new TelemetryClient(telemetryConfiguration); - telemetryClient.getContext().getProperties().put("JabRef version", Globals.BUILD_INFO.getVersion().toString()); + telemetryClient.getContext().getProperties().put("JabRef version", Globals.BUILD_INFO.version.toString()); telemetryClient.getContext().getProperties().put("Java version", StandardSystemProperty.JAVA_VERSION.value()); telemetryClient.getContext().getUser().setId(Globals.prefs.getOrCreateUserId()); telemetryClient.getContext().getSession().setId(UUID.randomUUID().toString()); diff --git a/src/main/java/org/jabref/JabRefGUI.java b/src/main/java/org/jabref/JabRefGUI.java index 3d9b20c3ba3..1ace4ffca4a 100644 --- a/src/main/java/org/jabref/JabRefGUI.java +++ b/src/main/java/org/jabref/JabRefGUI.java @@ -12,7 +12,6 @@ import javafx.stage.Stage; import org.jabref.gui.BasePanel; -import org.jabref.gui.GUIGlobals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.dialogs.BackupUIManager; import org.jabref.gui.help.VersionWorker; @@ -52,12 +51,12 @@ public JabRefGUI(Stage mainStage, List databases, boolean isBlank) mainFrame = new JabRefFrame(mainStage); openWindow(mainStage); - new VersionWorker(Globals.BUILD_INFO.getVersion(), Globals.prefs.getVersionPreferences().getIgnoredVersion(), mainFrame.getDialogService(), Globals.TASK_EXECUTOR) + new VersionWorker(Globals.BUILD_INFO.version, Globals.prefs.getVersionPreferences().getIgnoredVersion(), mainFrame.getDialogService(), Globals.TASK_EXECUTOR) .checkForNewVersionDelayed(); } private void openWindow(Stage mainStage) { - GUIGlobals.init(); + IconTheme.loadFonts(); LOGGER.debug("Initializing frame"); mainFrame.init(); diff --git a/src/main/java/org/jabref/JabRefMain.java b/src/main/java/org/jabref/JabRefMain.java index ec1cf738ac8..64caaf3e373 100644 --- a/src/main/java/org/jabref/JabRefMain.java +++ b/src/main/java/org/jabref/JabRefMain.java @@ -109,19 +109,19 @@ private static void ensureCorrectJavaVersion() { // Check if we are running an acceptable version of Java final BuildInfo buildInfo = Globals.BUILD_INFO; JavaVersion checker = new JavaVersion(); - final boolean java9Fail = !buildInfo.isAllowJava9() && checker.isJava9(); - final boolean versionFail = !checker.isAtLeast(buildInfo.getMinRequiredJavaVersion()); + final boolean java9Fail = !buildInfo.allowJava9 && checker.isJava9(); + final boolean versionFail = !checker.isAtLeast(buildInfo.minRequiredJavaVersion); if (java9Fail || versionFail) { StringBuilder versionError = new StringBuilder( Localization.lang("Your current Java version (%0) is not supported. Please install version %1 or higher.", checker.getJavaVersion(), - buildInfo.getMinRequiredJavaVersion())); + buildInfo.minRequiredJavaVersion)); versionError.append("\n"); versionError.append(Localization.lang("Your Java Runtime Environment is located at %0.", checker.getJavaInstallationDirectory())); - if (!buildInfo.isAllowJava9()) { + if (!buildInfo.allowJava9) { versionError.append("\n"); versionError.append(Localization.lang("Note that currently, JabRef does not run with Java 9.")); } diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java index c9d01d6784d..16f0ede5aea 100644 --- a/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -431,7 +431,7 @@ private void exportFile(List loaded, String[] data) { theFile = theFile.getAbsoluteFile(); } BibDatabaseContext databaseContext = pr.getDatabaseContext(); - databaseContext.setDatabaseFile(theFile); + databaseContext.setDatabasePath(theFile.toPath()); Globals.prefs.fileDirForDatabase = databaseContext .getFileDirectories(Globals.prefs.getFilePreferences()); System.out.println(Localization.lang("Exporting") + ": " + data[0]); diff --git a/src/main/java/org/jabref/cli/JabRefCLI.java b/src/main/java/org/jabref/cli/JabRefCLI.java index a279a266aa4..0f3baecdf59 100644 --- a/src/main/java/org/jabref/cli/JabRefCLI.java +++ b/src/main/java/org/jabref/cli/JabRefCLI.java @@ -255,7 +255,7 @@ public static void printUsage() { } private String getVersionInfo() { - return String.format("JabRef %s", Globals.BUILD_INFO.getVersion()); + return String.format("JabRef %s", Globals.BUILD_INFO.version); } public List getLeftOver() { diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index efec6542ab0..330463448ed 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -1,20 +1,10 @@ package org.jabref.gui; -import java.io.IOException; -import java.io.StringReader; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.swing.undo.CannotRedoException; -import javax.swing.undo.CannotUndoException; import javafx.application.Platform; import javafx.beans.binding.Bindings; @@ -25,55 +15,28 @@ import org.jabref.Globals; import org.jabref.JabRefExecutorService; -import org.jabref.gui.actions.Actions; -import org.jabref.gui.actions.BaseAction; import org.jabref.gui.autocompleter.AutoCompletePreferences; import org.jabref.gui.autocompleter.AutoCompleteUpdater; import org.jabref.gui.autocompleter.PersonNameSuggestionProvider; import org.jabref.gui.autocompleter.SuggestionProviders; -import org.jabref.gui.bibtexkeypattern.GenerateBibtexKeyAction; -import org.jabref.gui.cleanup.CleanupAction; import org.jabref.gui.collab.DatabaseChangeMonitor; import org.jabref.gui.collab.DatabaseChangePane; -import org.jabref.gui.desktop.JabRefDesktop; -import org.jabref.gui.edit.CopyBibTeXKeyAndLinkAction; -import org.jabref.gui.edit.ReplaceStringAction; import org.jabref.gui.entryeditor.EntryEditor; -import org.jabref.gui.exporter.SaveDatabaseAction; -import org.jabref.gui.exporter.WriteXMPAction; -import org.jabref.gui.externalfiles.DownloadFullTextAction; -import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; -import org.jabref.gui.importer.actions.AppendDatabaseAction; -import org.jabref.gui.journals.AbbreviateAction; -import org.jabref.gui.journals.AbbreviationType; -import org.jabref.gui.journals.UnabbreviateAction; import org.jabref.gui.maintable.MainTable; import org.jabref.gui.maintable.MainTableDataModel; -import org.jabref.gui.mergeentries.MergeEntriesAction; -import org.jabref.gui.mergeentries.MergeWithFetchedEntryAction; -import org.jabref.gui.preview.CitationStyleToClipboardWorker; import org.jabref.gui.specialfields.SpecialFieldDatabaseChangeListener; -import org.jabref.gui.specialfields.SpecialFieldValueViewModel; -import org.jabref.gui.specialfields.SpecialFieldViewModel; import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.undo.UndoableInsertEntries; import org.jabref.gui.undo.UndoableRemoveEntries; import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.gui.worker.SendAsEMailAction; import org.jabref.logic.citationstyle.CitationStyleCache; -import org.jabref.logic.citationstyle.CitationStyleOutputFormat; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.layout.Layout; -import org.jabref.logic.layout.LayoutHelper; import org.jabref.logic.pdf.FileAnnotationCache; import org.jabref.logic.search.SearchQuery; import org.jabref.logic.util.UpdateField; -import org.jabref.logic.util.io.FileFinder; -import org.jabref.logic.util.io.FileFinders; -import org.jabref.logic.util.io.FileUtil; import org.jabref.model.FieldChange; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; @@ -83,17 +46,11 @@ import org.jabref.model.database.event.EntriesAddedEvent; import org.jabref.model.database.event.EntriesRemovedEvent; import org.jabref.model.database.shared.DatabaseLocation; -import org.jabref.model.database.shared.DatabaseSynchronizer; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.FileFieldParser; -import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.event.EntriesEventSource; import org.jabref.model.entry.event.EntryChangedEvent; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; -import org.jabref.model.entry.field.SpecialField; -import org.jabref.model.entry.field.SpecialFieldValue; -import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.JabRefPreferences; import com.google.common.eventbus.Subscribe; @@ -113,12 +70,8 @@ public class BasePanel extends StackPane { private final FileAnnotationCache annotationCache; private final JabRefFrame frame; - // The undo manager. - private final UndoAction undoAction = new UndoAction(); - private final RedoAction redoAction = new RedoAction(); private final CountingUndoManager undoManager; - // Keeps track of the string dialog if it is open. - private final Map actions = new HashMap<>(); + private final SidePaneManager sidePaneManager; private final ExternalFileTypes externalFileTypes; @@ -126,13 +79,10 @@ public class BasePanel extends StackPane { private final DialogService dialogService; private MainTable mainTable; private BasePanelPreferences preferences; - // To contain instantiated entry editors. This is to save time - // As most enums, this must not be null private BasePanelMode mode = BasePanelMode.SHOWING_NOTHING; private SplitPane splitPane; private DatabaseChangePane changePane; private boolean saving; - // AutoCompleter used in the search bar private PersonNameSuggestionProvider searchAutoCompleter; private boolean baseChanged; private boolean nonUndoableChange; @@ -165,8 +115,6 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas setupMainPanel(); - setupActions(); - this.getDatabase().registerListener(new SearchListener()); this.getDatabase().registerListener(new EntriesRemovedListener()); @@ -179,7 +127,7 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas this.entryEditor = new EntryEditor(this, externalFileTypes); // Open entry editor for first entry on start up. - Platform.runLater(() -> clearAndSelectFirst()); + Platform.runLater(this::clearAndSelectFirst); } @Subscribe @@ -200,12 +148,12 @@ public String getTabTitle() { boolean isAutosaveEnabled = Globals.prefs.getBoolean(JabRefPreferences.LOCAL_AUTO_SAVE); if (databaseLocation == DatabaseLocation.LOCAL) { - if (this.bibDatabaseContext.getDatabaseFile().isPresent()) { + if (this.bibDatabaseContext.getDatabasePath().isPresent()) { // check if file is modified String changeFlag = isModified() && !isAutosaveEnabled ? "*" : ""; - title.append(this.bibDatabaseContext.getDatabaseFile().get().getName()).append(changeFlag); + title.append(this.bibDatabaseContext.getDatabasePath().get().getFileName()).append(changeFlag); } else { - title.append(GUIGlobals.UNTITLED_TITLE); + title.append(Localization.lang("untitled")); if (getDatabase().hasEntries()) { // if the database is not empty and no file is assigned, @@ -242,148 +190,6 @@ public void output(String s) { dialogService.notify(s); } - private void setupActions() { - SaveDatabaseAction saveAction = new SaveDatabaseAction(this, Globals.prefs, Globals.entryTypesManager); - CleanupAction cleanUpAction = new CleanupAction(this, Globals.prefs, Globals.TASK_EXECUTOR); - - actions.put(Actions.UNDO, undoAction); - actions.put(Actions.REDO, redoAction); - - // The action for opening an entry editor. - actions.put(Actions.EDIT, this::showAndEdit); - - // The action for saving a database. - actions.put(Actions.SAVE, saveAction::save); - - actions.put(Actions.SAVE_AS, saveAction::saveAs); - - actions.put(Actions.SAVE_SELECTED_AS_PLAIN, saveAction::saveSelectedAsPlain); - - // The action for copying selected entries. - actions.put(Actions.COPY, this::copy); - - actions.put(Actions.CUT, this::cut); - - actions.put(Actions.DELETE, () -> delete(false)); - - // The action for pasting entries or cell contents. - // - more robust detection of available content flavors (doesn't only look at first one offered) - // - support for parsing string-flavor clipboard contents which are bibtex entries. - // This allows you to (a) paste entire bibtex entries from a text editor, web browser, etc - // (b) copy and paste entries between multiple instances of JabRef (since - // only the text representation seems to get as far as the X clipboard, at least on my system) - actions.put(Actions.PASTE, this::paste); - - actions.put(Actions.SELECT_ALL, mainTable.getSelectionModel()::selectAll); - - // The action for auto-generating keys. - actions.put(Actions.MAKE_KEY, new GenerateBibtexKeyAction(this, frame.getDialogService())); - - // The action for cleaning up entry. - actions.put(Actions.CLEANUP, cleanUpAction); - - actions.put(Actions.MERGE_ENTRIES, () -> new MergeEntriesAction(frame, Globals.stateManager).execute()); - - // The action for copying the selected entry's key. - actions.put(Actions.COPY_KEY, this::copyKey); - - // The action for copying the selected entry's title. - actions.put(Actions.COPY_TITLE, this::copyTitle); - - // The action for copying a cite for the selected entry. - actions.put(Actions.COPY_CITE_KEY, this::copyCiteKey); - - // The action for copying the BibTeX key and the title for the first selected entry - actions.put(Actions.COPY_KEY_AND_TITLE, this::copyKeyAndTitle); - - actions.put(Actions.COPY_CITATION_ASCII_DOC, () -> copyCitationToClipboard(CitationStyleOutputFormat.ASCII_DOC)); - actions.put(Actions.COPY_CITATION_XSLFO, () -> copyCitationToClipboard(CitationStyleOutputFormat.XSL_FO)); - actions.put(Actions.COPY_CITATION_HTML, () -> copyCitationToClipboard(CitationStyleOutputFormat.HTML)); - actions.put(Actions.COPY_CITATION_RTF, () -> copyCitationToClipboard(CitationStyleOutputFormat.RTF)); - actions.put(Actions.COPY_CITATION_TEXT, () -> copyCitationToClipboard(CitationStyleOutputFormat.TEXT)); - - // The action for copying the BibTeX keys as hyperlinks to the urls of the selected entries - actions.put(Actions.COPY_KEY_AND_LINK, new CopyBibTeXKeyAndLinkAction(mainTable, Globals.clipboardManager)); - - actions.put(Actions.MERGE_DATABASE, new AppendDatabaseAction(frame, this)); - - actions.put(Actions.OPEN_EXTERNAL_FILE, this::openExternalFile); - - actions.put(Actions.OPEN_FOLDER, () -> JabRefExecutorService.INSTANCE.execute(() -> { - final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())); - for (final Path f : files) { - try { - JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath()); - } catch (IOException e) { - LOGGER.info("Could not open folder", e); - } - } - })); - - actions.put(Actions.OPEN_CONSOLE, () -> JabRefDesktop.openConsole(frame.getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null))); - - actions.put(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, () -> { - DatabaseSynchronizer dbmsSynchronizer = frame.getCurrentBasePanel().getBibDatabaseContext().getDBMSSynchronizer(); - dbmsSynchronizer.pullChanges(); - }); - - actions.put(Actions.OPEN_URL, new OpenURLAction()); - - actions.put(Actions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(this, frame.getDialogService())); - - actions.put(Actions.REPLACE_ALL, () -> (new ReplaceStringAction(this)).execute()); - - actions.put(new SpecialFieldValueViewModel(SpecialField.RELEVANCE.getValues().get(0)).getCommand(), - new SpecialFieldViewModel(SpecialField.RELEVANCE, undoManager).getSpecialFieldAction(SpecialField.RELEVANCE.getValues().get(0), frame)); - - actions.put(new SpecialFieldValueViewModel(SpecialField.QUALITY.getValues().get(0)).getCommand(), - new SpecialFieldViewModel(SpecialField.QUALITY, undoManager).getSpecialFieldAction(SpecialField.QUALITY.getValues().get(0), frame)); - - actions.put(new SpecialFieldValueViewModel(SpecialField.PRINTED.getValues().get(0)).getCommand(), - new SpecialFieldViewModel(SpecialField.PRINTED, undoManager).getSpecialFieldAction(SpecialField.PRINTED.getValues().get(0), frame)); - - for (SpecialFieldValue prio : SpecialField.PRIORITY.getValues()) { - actions.put(new SpecialFieldValueViewModel(prio).getCommand(), - new SpecialFieldViewModel(SpecialField.PRIORITY, undoManager).getSpecialFieldAction(prio, this.frame)); - } - for (SpecialFieldValue rank : SpecialField.RANKING.getValues()) { - actions.put(new SpecialFieldValueViewModel(rank).getCommand(), - new SpecialFieldViewModel(SpecialField.RANKING, undoManager).getSpecialFieldAction(rank, this.frame)); - } - for (SpecialFieldValue status : SpecialField.READ_STATUS.getValues()) { - actions.put(new SpecialFieldValueViewModel(status).getCommand(), - new SpecialFieldViewModel(SpecialField.READ_STATUS, undoManager).getSpecialFieldAction(status, this.frame)); - } - - actions.put(Actions.NEXT_PREVIEW_STYLE, () -> { - entryEditor.nextPreviewStyle(); - }); - actions.put(Actions.PREVIOUS_PREVIEW_STYLE, () -> { - entryEditor.previousPreviewStyle(); - }); - - actions.put(Actions.SEND_AS_EMAIL, new SendAsEMailAction(frame)); - - actions.put(Actions.WRITE_XMP, new WriteXMPAction(this)::execute); - - actions.put(Actions.ABBREVIATE_DEFAULT, new AbbreviateAction(this, AbbreviationType.DEFAULT)); - actions.put(Actions.ABBREVIATE_MEDLINE, new AbbreviateAction(this, AbbreviationType.MEDLINE)); - actions.put(Actions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(this, AbbreviationType.SHORTEST_UNIQUE)); - actions.put(Actions.UNABBREVIATE, new UnabbreviateAction(this)); - - actions.put(Actions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(this)::execute); - } - - /** - * Generates and copies citations based on the selected entries to the clipboard - * - * @param outputFormat the desired {@link CitationStyleOutputFormat} - */ - private void copyCitationToClipboard(CitationStyleOutputFormat outputFormat) { - CitationStyleToClipboardWorker worker = new CitationStyleToClipboardWorker(this, outputFormat, dialogService, Globals.clipboardManager, Globals.prefs.getPreviewPreferences()); - worker.copyCitationStyleToClipboard(Globals.TASK_EXECUTOR); - } - /** * Removes the selected entries from the database * @@ -423,177 +229,6 @@ public void delete(BibEntry entry) { delete(false, Collections.singletonList(entry)); } - private void copyTitle() { - List selectedBibEntries = mainTable.getSelectedEntries(); - if (!selectedBibEntries.isEmpty()) { - // Collect all non-null titles. - List titles = selectedBibEntries.stream() - .filter(bibEntry -> bibEntry.getTitle().isPresent()) - .map(bibEntry -> bibEntry.getTitle().get()) - .collect(Collectors.toList()); - - if (titles.isEmpty()) { - output(Localization.lang("None of the selected entries have titles.")); - return; - } - final String copiedTitles = String.join("\n", titles); - Globals.clipboardManager.setContent(copiedTitles); - - if (titles.size() == selectedBibEntries.size()) { - // All entries had titles. - output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedTitles) + "'."); - } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined title.", Integer.toString(selectedBibEntries.size() - titles.size()), Integer.toString(selectedBibEntries.size()))); - } - } - } - - private void copyCiteKey() { - List bes = mainTable.getSelectedEntries(); - if (!bes.isEmpty()) { - List keys = new ArrayList<>(bes.size()); - // Collect all non-null keys. - for (BibEntry be : bes) { - be.getCiteKeyOptional().ifPresent(keys::add); - } - if (keys.isEmpty()) { - output(Localization.lang("None of the selected entries have BibTeX keys.")); - return; - } - - String citeCommand = Optional.ofNullable(Globals.prefs.get(JabRefPreferences.CITE_COMMAND)) - .filter(cite -> cite.contains("\\")) // must contain \ - .orElse("\\cite"); - final String copiedCiteCommand = citeCommand + "{" + String.join(",", keys) + '}'; - Globals.clipboardManager.setContent(copiedCiteCommand); - - if (keys.size() == bes.size()) { - // All entries had keys. - output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedCiteCommand) + "'."); - } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size()))); - } - } - } - - private void copyKey() { - List bes = mainTable.getSelectedEntries(); - if (!bes.isEmpty()) { - List keys = new ArrayList<>(bes.size()); - // Collect all non-null keys. - for (BibEntry be : bes) { - be.getCiteKeyOptional().ifPresent(keys::add); - } - if (keys.isEmpty()) { - output(Localization.lang("None of the selected entries have BibTeX keys.")); - return; - } - - final String copiedKeys = String.join(",", keys); - Globals.clipboardManager.setContent(copiedKeys); - - if (keys.size() == bes.size()) { - // All entries had keys. - output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedKeys) + "'."); - } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - keys.size()), Integer.toString(bes.size()))); - } - } - } - - private void copyKeyAndTitle() { - List bes = mainTable.getSelectedEntries(); - if (!bes.isEmpty()) { - // OK: in a future version, this string should be configurable to allow arbitrary exports - StringReader sr = new StringReader("\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n"); - Layout layout; - try { - layout = new LayoutHelper(sr, Globals.prefs.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader)) - .getLayoutFromText(); - } catch (IOException e) { - LOGGER.info("Could not get layout", e); - return; - } - - StringBuilder sb = new StringBuilder(); - - int copied = 0; - // Collect all non-null keys. - for (BibEntry be : bes) { - if (be.hasCiteKey()) { - copied++; - sb.append(layout.doLayout(be, bibDatabaseContext.getDatabase())); - } - } - - if (copied == 0) { - output(Localization.lang("None of the selected entries have BibTeX keys.")); - return; - } - - final String copiedKeysAndTitles = sb.toString(); - Globals.clipboardManager.setContent(copiedKeysAndTitles); - - if (copied == bes.size()) { - // All entries had keys. - output(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(copiedKeysAndTitles) + "'."); - } else { - output(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", Integer.toString(bes.size() - copied), Integer.toString(bes.size()))); - } - } - } - - private void openExternalFile() { - final List selectedEntries = mainTable.getSelectedEntries(); - if (selectedEntries.size() != 1) { - output(Localization.lang("This operation requires exactly one item to be selected.")); - return; - } - JabRefExecutorService.INSTANCE.execute(() -> { - final BibEntry entry = selectedEntries.get(0); - if (!entry.hasField(StandardField.FILE)) { - // no bibtex field - new SearchAndOpenFile(entry, BasePanel.this).searchAndOpen(); - return; - } - - List files = new ArrayList<>(); - entry.getField(StandardField.FILE).map(FileFieldParser::parse).ifPresent(files::addAll); - - if (files.isEmpty()) { - // content in BibTeX field is not readable - new SearchAndOpenFile(entry, BasePanel.this).searchAndOpen(); - return; - } - LinkedFile flEntry = files.get(0); - try { - JabRefDesktop.openExternalFileAnyFormat(this.getBibDatabaseContext(), flEntry.getLink(), ExternalFileTypes.getInstance().fromLinkedFile(flEntry, true)); - } catch (IOException ex) { - dialogService.showErrorDialogAndWait(ex); - } - }); - } - - /** - * This method is called from JabRefFrame if a database specific action is requested by the user. Runs the command - * if it is defined, or prints an error message to the standard error stream. - * - * @param command The name of the command to run. - */ - public void runCommand(final Actions command) { - if (!actions.containsKey(command)) { - LOGGER.info("No action defined for '" + command + '\''); - return; - } - - BaseAction action = actions.get(command); - try { - action.action(); - } catch (Throwable ex) { - LOGGER.error("runCommand error: " + ex.getMessage(), ex); - } - } - public void registerUndoableChanges(List changes) { NamedCompound ce = new NamedCompound(Localization.lang("Save actions")); for (FieldChange change : changes) { @@ -651,17 +286,11 @@ public void editEntryAndFocusField(BibEntry entry, Field field) { }); } - public void updateTableFont() { - mainTable.updateFont(); - } - private void createMainTable() { bibDatabaseContext.getDatabase().registerListener(SpecialFieldDatabaseChangeListener.INSTANCE); mainTable = new MainTable(tableModel, frame, this, bibDatabaseContext, preferences.getTablePreferences(), externalFileTypes, preferences.getKeyBindings()); - mainTable.updateFont(); - // Add the listener that binds selection to state manager (TODO: should be replaced by proper JavaFX binding as soon as table is implemented in JavaFX) mainTable.addSelectionListener(listEvent -> Globals.stateManager.setSelectedEntries(mainTable.getSelectedEntries())); @@ -844,12 +473,6 @@ private void showBottomPane(BasePanelMode newMode) { adjustSplitter(); } - private void showAndEdit() { - if (!mainTable.getSelectedEntries().isEmpty()) { - showAndEdit(mainTable.getSelectedEntries().get(0)); - } - } - /** * Removes the bottom component. */ @@ -871,7 +494,9 @@ public void clearAndSelect(final BibEntry bibEntry) { */ private void clearAndSelectFirst() { mainTable.clearAndSelectFirst(); - showAndEdit(); + if (!mainTable.getSelectedEntries().isEmpty()) { + showAndEdit(mainTable.getSelectedEntries().get(0)); + } } public void selectPreviousEntry() { @@ -922,17 +547,17 @@ public void markNonUndoableBaseChanged() { markBaseChanged(); } - private synchronized void markChangedOrUnChanged() { + public synchronized void markChangedOrUnChanged() { if (getUndoManager().hasChanged()) { if (!baseChanged) { markBaseChanged(); } } else if (baseChanged && !nonUndoableChange) { baseChanged = false; - if (getBibDatabaseContext().getDatabaseFile().isPresent()) { - frame.setTabTitle(this, getTabTitle(), getBibDatabaseContext().getDatabaseFile().get().getAbsolutePath()); + if (getBibDatabaseContext().getDatabasePath().isPresent()) { + frame.setTabTitle(this, getTabTitle(), getBibDatabaseContext().getDatabasePath().get().toAbsolutePath().toString()); } else { - frame.setTabTitle(this, GUIGlobals.UNTITLED_TITLE, null); + frame.setTabTitle(this, Localization.lang("untitled"), null); } } frame.setWindowTitle(); @@ -1077,47 +702,6 @@ public void cut() { mainTable.cut(); } - private static class SearchAndOpenFile { - - private final BibEntry entry; - private final BasePanel basePanel; - - public SearchAndOpenFile(final BibEntry entry, final BasePanel basePanel) { - this.entry = entry; - this.basePanel = basePanel; - } - - public void searchAndOpen() { - if (!Globals.prefs.getBoolean(JabRefPreferences.RUN_AUTOMATIC_FILE_SEARCH)) { - /* The search can lead to an unexpected 100% CPU usage which is perceived - as a bug, if the search incidentally starts at a directory with lots - of stuff below. It is now disabled by default. */ - return; - } - - final Set types = ExternalFileTypes.getInstance().getExternalFileTypeSelection(); - final List dirs = basePanel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); - final List extensions = types.stream().map(ExternalFileType::getExtension).collect(Collectors.toList()); - - // Run the search operation: - FileFinder fileFinder = FileFinders.constructFromConfiguration(Globals.prefs.getAutoLinkPreferences()); - try { - List files = fileFinder.findAssociatedFiles(entry, dirs, extensions); - if (!files.isEmpty()) { - Path file = files.get(0); - Optional type = ExternalFileTypes.getInstance().getExternalFileTypeByFile(file); - if (type.isPresent()) { - JabRefDesktop.openExternalFileAnyFormat(file, basePanel.getBibDatabaseContext(), type); - basePanel.output(Localization.lang("External viewer called") + '.'); - } - } - } catch (IOException ex) { - LOGGER.error("Problems with finding/or opening files ", ex); - basePanel.output(Localization.lang("Error") + ": " + ex.getMessage()); - } - } - } - private class GroupTreeListener { @Subscribe @@ -1182,91 +766,4 @@ public void listen(EntriesRemovedEvent removedEntriesEvent) { DefaultTaskExecutor.runInJavaFXThread(() -> frame.getGlobalSearchBar().performSearch()); } } - - private class UndoAction implements BaseAction { - - @Override - public void action() { - try { - getUndoManager().undo(); - markBaseChanged(); - output(Localization.lang("Undo")); - } catch (CannotUndoException ex) { - LOGGER.warn("Nothing to undo", ex); - output(Localization.lang("Nothing to undo") + '.'); - } - - markChangedOrUnChanged(); - } - } - - private class OpenURLAction implements BaseAction { - - @Override - public void action() { - final List bes = mainTable.getSelectedEntries(); - if (bes.size() == 1) { - Field field = StandardField.DOI; - Optional link = bes.get(0).getField(StandardField.DOI); - if (bes.get(0).hasField(StandardField.URL)) { - link = bes.get(0).getField(StandardField.URL); - field = StandardField.URL; - } - if (link.isPresent()) { - try { - JabRefDesktop.openExternalViewer(bibDatabaseContext, link.get(), field); - output(Localization.lang("External viewer called") + '.'); - } catch (IOException ex) { - output(Localization.lang("Error") + ": " + ex.getMessage()); - } - } else { - // No URL or DOI found in the "url" and "doi" fields. - // Look for web links in the "file" field as a fallback: - - List files = bes.get(0).getFiles(); - - Optional linkedFile = files.stream() - .filter(file -> (StandardField.URL.getName().equalsIgnoreCase(file.getFileType()) - || StandardField.PS.getName().equalsIgnoreCase(file.getFileType()) - || StandardField.PDF.getName().equalsIgnoreCase(file.getFileType()))) - .findFirst(); - - if (linkedFile.isPresent()) { - - try { - - JabRefDesktop.openExternalFileAnyFormat(bibDatabaseContext, - linkedFile.get().getLink(), - ExternalFileTypes.getInstance().fromLinkedFile(linkedFile.get(), true)); - - output(Localization.lang("External viewer called") + '.'); - } catch (IOException e) { - output(Localization.lang("Could not open link")); - LOGGER.info("Could not open link", e); - } - } else { - output(Localization.lang("No URL defined") + '.'); - } - } - } else { - output(Localization.lang("This operation requires exactly one item to be selected.")); - } - } - } - - private class RedoAction implements BaseAction { - - @Override - public void action() { - try { - getUndoManager().redo(); - markBaseChanged(); - output(Localization.lang("Redo")); - } catch (CannotRedoException ex) { - output(Localization.lang("Nothing to redo") + '.'); - } - - markChangedOrUnChanged(); - } - } } diff --git a/src/main/java/org/jabref/gui/GUIGlobals.java b/src/main/java/org/jabref/gui/GUIGlobals.java deleted file mode 100644 index 21c19c98081..00000000000 --- a/src/main/java/org/jabref/gui/GUIGlobals.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.jabref.gui; - -import javafx.scene.paint.Color; -import javafx.scene.text.Font; - -import org.jabref.Globals; -import org.jabref.gui.icon.IconTheme; -import org.jabref.gui.util.CustomLocalDragboard; -import org.jabref.logic.l10n.Localization; -import org.jabref.preferences.JabRefPreferences; - -/** - * Static variables for graphics files and keyboard shortcuts. - */ -public class GUIGlobals { - - public static Color editorTextColor; - public static Color validFieldBackgroundColor; - public static Color activeBackgroundColor; - public static Color invalidFieldBackgroundColor; - public static Font currentFont; - - public static CustomLocalDragboard localDragboard = new CustomLocalDragboard(); - - public static final String UNTITLED_TITLE = Localization.lang("untitled"); - - private GUIGlobals() { - } - - public static void updateEntryEditorColors() { - GUIGlobals.activeBackgroundColor = JabRefPreferences.getInstance().getColor(JabRefPreferences.ACTIVE_FIELD_EDITOR_BACKGROUND_COLOR); - GUIGlobals.validFieldBackgroundColor = JabRefPreferences.getInstance().getColor(JabRefPreferences.VALID_FIELD_BACKGROUND_COLOR); - GUIGlobals.invalidFieldBackgroundColor = JabRefPreferences.getInstance().getColor(JabRefPreferences.INVALID_FIELD_BACKGROUND_COLOR); - GUIGlobals.editorTextColor = JabRefPreferences.getInstance().getColor(JabRefPreferences.FIELD_EDITOR_TEXT_COLOR); - } - - /** - * Perform initializations that are only used in graphical mode. This is to prevent - * the "Xlib: connection to ":0.0" refused by server" error when access to the X server - * on Un*x is unavailable. - */ - public static void init() { - // Set up entry editor colors, first time: - GUIGlobals.updateEntryEditorColors(); - - IconTheme.loadFonts(); - GUIGlobals.currentFont = new Font(Globals.prefs.getFontFamily(), Globals.prefs.getDouble(JabRefPreferences.FONT_SIZE)); - } - - public static void setFont(double size) { - currentFont = new Font(currentFont.getFamily(), size); - // update preferences - Globals.prefs.putInt(JabRefPreferences.FONT_SIZE, size); - } -} diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index c7bc0a63088..462786823ae 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -1,10 +1,10 @@ package org.jabref.gui; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,7 +30,6 @@ import javafx.scene.control.SplitPane; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; -import javafx.scene.control.TextInputControl; import javafx.scene.control.ToolBar; import javafx.scene.control.Tooltip; import javafx.scene.control.skin.TabPaneSkin; @@ -46,29 +45,37 @@ import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.actions.ActionFactory; -import org.jabref.gui.actions.Actions; -import org.jabref.gui.actions.OldDatabaseCommandWrapper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.actions.StandardActions; import org.jabref.gui.auximport.NewSubLibraryAction; import org.jabref.gui.bibtexextractor.ExtractBibtexAction; import org.jabref.gui.bibtexkeypattern.BibtexKeyPatternAction; +import org.jabref.gui.bibtexkeypattern.GenerateBibtexKeyAction; +import org.jabref.gui.cleanup.CleanupAction; import org.jabref.gui.contentselector.ManageContentSelectorAction; import org.jabref.gui.copyfiles.CopyFilesAction; import org.jabref.gui.customentrytypes.CustomizeEntryAction; import org.jabref.gui.customizefields.SetupGeneralFieldsAction; -import org.jabref.gui.dialogs.AutosaveUIManager; +import org.jabref.gui.dialogs.AutosaveUiManager; import org.jabref.gui.documentviewer.ShowDocumentViewerAction; import org.jabref.gui.duplicationFinder.DuplicateSearch; +import org.jabref.gui.edit.CopyMoreAction; +import org.jabref.gui.edit.EditAction; import org.jabref.gui.edit.ManageKeywordsAction; import org.jabref.gui.edit.MassSetFieldsAction; import org.jabref.gui.edit.OpenBrowserAction; +import org.jabref.gui.edit.ReplaceStringAction; +import org.jabref.gui.entryeditor.OpenEntryEditorAction; +import org.jabref.gui.entryeditor.PreviewSwitchAction; import org.jabref.gui.exporter.ExportCommand; import org.jabref.gui.exporter.ExportToClipboardAction; import org.jabref.gui.exporter.ManageCustomExportsAction; +import org.jabref.gui.exporter.SaveAction; import org.jabref.gui.exporter.SaveAllAction; import org.jabref.gui.exporter.SaveDatabaseAction; +import org.jabref.gui.exporter.WriteXMPAction; import org.jabref.gui.externalfiles.AutoLinkFilesAction; +import org.jabref.gui.externalfiles.DownloadFullTextAction; import org.jabref.gui.externalfiles.FindUnlinkedFilesAction; import org.jabref.gui.externalfiletype.EditExternalFileTypesAction; import org.jabref.gui.externalfiletype.ExternalFileTypes; @@ -81,29 +88,36 @@ import org.jabref.gui.importer.ManageCustomImportsAction; import org.jabref.gui.importer.NewDatabaseAction; import org.jabref.gui.importer.NewEntryAction; +import org.jabref.gui.importer.actions.AppendDatabaseAction; import org.jabref.gui.importer.actions.OpenDatabaseAction; import org.jabref.gui.importer.fetcher.LookupIdentifierAction; import org.jabref.gui.integrity.IntegrityCheckAction; +import org.jabref.gui.journals.AbbreviateAction; import org.jabref.gui.journals.ManageJournalsAction; import org.jabref.gui.keyboard.CustomizeKeyBindingAction; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.gui.libraryproperties.LibraryPropertiesAction; import org.jabref.gui.menus.FileHistoryMenu; +import org.jabref.gui.mergeentries.MergeEntriesAction; import org.jabref.gui.metadata.BibtexStringEditorAction; import org.jabref.gui.metadata.PreambleEditor; import org.jabref.gui.preferences.ShowPreferencesAction; +import org.jabref.gui.preview.CopyCitationAction; import org.jabref.gui.protectedterms.ManageProtectedTermsAction; import org.jabref.gui.push.PushToApplicationAction; import org.jabref.gui.push.PushToApplicationsManager; import org.jabref.gui.search.GlobalSearchBar; import org.jabref.gui.shared.ConnectToSharedDatabaseCommand; +import org.jabref.gui.shared.PullChangesFromSharedAction; import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory; -import org.jabref.gui.texparser.ParseTexAction; +import org.jabref.gui.texparser.ParseLatexAction; import org.jabref.gui.undo.CountingUndoManager; +import org.jabref.gui.undo.UndoRedoAction; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.logic.autosaveandbackup.AutosaveManager; import org.jabref.logic.autosaveandbackup.BackupManager; +import org.jabref.logic.citationstyle.CitationStyleOutputFormat; import org.jabref.logic.importer.IdFetcher; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.WebFetchers; @@ -228,18 +242,6 @@ private void initKeyBindings() { tabbedPane.getSelectionModel().selectPrevious(); event.consume(); break; - case INCREASE_TABLE_FONT_SIZE: - increaseTableFontSize(); - event.consume(); - break; - case DECREASE_TABLE_FONT_SIZE: - decreaseTableFontSize(); - event.consume(); - break; - case DEFAULT_TABLE_FONT_SIZE: - setDefaultTableFontSize(); - event.consume(); - break; case SEARCH: getGlobalSearchBar().focus(); break; @@ -303,9 +305,9 @@ public void setWindowTitle() { if (panel.getBibDatabaseContext().getLocation() == DatabaseLocation.LOCAL) { String changeFlag = panel.isModified() && !isAutosaveEnabled ? "*" : ""; String databaseFile = panel.getBibDatabaseContext() - .getDatabaseFile() - .map(File::getPath) - .orElse(GUIGlobals.UNTITLED_TITLE); + .getDatabasePath() + .map(Path::toString) + .orElse(Localization.lang("untitled")); //setTitle(FRAME_TITLE + " - " + databaseFile + changeFlag + modeInfo); } else if (panel.getBibDatabaseContext().getLocation() == DatabaseLocation.SHARED) { //setTitle(FRAME_TITLE + " - " + panel.getBibDatabaseContext().getDBMSSynchronizer().getDBName() + " [" @@ -352,7 +354,7 @@ private void tearDownJabRef(List filenames) { prefs.remove(JabRefPreferences.LAST_EDITED); } else { prefs.putStringList(JabRefPreferences.LAST_EDITED, filenames); - File focusedDatabase = getCurrentBasePanel().getBibDatabaseContext().getDatabaseFile().orElse(null); + Path focusedDatabase = getCurrentBasePanel().getBibDatabaseContext().getDatabasePath().orElse(null); new LastFocusedTabPreferences(prefs).setLastFocusedTab(focusedDatabase); } } @@ -391,7 +393,7 @@ public boolean quit() { } AutosaveManager.shutdown(context); BackupManager.shutdown(context); - context.getDatabaseFile().map(File::getAbsolutePath).ifPresent(filenames::add); + context.getDatabasePath().map(Path::toAbsolutePath).map(Path::toString).ifPresent(filenames::add); } WaitForSaveFinishedDialog waitForSaveFinishedDialog = new WaitForSaveFinishedDialog(dialogService); @@ -415,7 +417,7 @@ private void initLayout() { splitPane.getItems().addAll(sidePane, tabbedPane); // We need to wait with setting the divider since it gets reset a few times during the initial set-up - mainStage.showingProperty().addListener(new ChangeListener() { + mainStage.showingProperty().addListener(new ChangeListener<>() { @Override public void changed(ObservableValue observable, Boolean oldValue, Boolean showing) { @@ -469,7 +471,7 @@ private Node createToolbar() { HBox leftSide = new HBox( newLibrary, factory.createIconButton(StandardActions.OPEN_LIBRARY, new OpenDatabaseAction(this)), - factory.createIconButton(StandardActions.SAVE_LIBRARY, new OldDatabaseCommandWrapper(Actions.SAVE, this, stateManager)), + factory.createIconButton(StandardActions.SAVE_LIBRARY, new SaveAction(SaveAction.SaveMethod.SAVE, this, stateManager)), leftSpacer ); @@ -481,17 +483,17 @@ private Node createToolbar() { factory.createIconButton(StandardActions.NEW_ARTICLE, new NewEntryAction(this, StandardEntryType.Article, dialogService, Globals.prefs, stateManager)), factory.createIconButton(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs, stateManager)), factory.createIconButton(StandardActions.NEW_ENTRY_FROM_PLAIN_TEXT, new ExtractBibtexAction(stateManager)), - factory.createIconButton(StandardActions.DELETE_ENTRY, new OldDatabaseCommandWrapper(Actions.DELETE, this, stateManager)), + factory.createIconButton(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, this, stateManager)), new Separator(Orientation.VERTICAL), - factory.createIconButton(StandardActions.UNDO, new OldDatabaseCommandWrapper(Actions.UNDO, this, stateManager)), - factory.createIconButton(StandardActions.REDO, new OldDatabaseCommandWrapper(Actions.REDO, this, stateManager)), - factory.createIconButton(StandardActions.CUT, new OldDatabaseCommandWrapper(Actions.CUT, this, stateManager)), - factory.createIconButton(StandardActions.COPY, new OldDatabaseCommandWrapper(Actions.COPY, this, stateManager)), - factory.createIconButton(StandardActions.PASTE, new OldDatabaseCommandWrapper(Actions.PASTE, this, stateManager)), + factory.createIconButton(StandardActions.UNDO, new UndoRedoAction(StandardActions.UNDO, this, dialogService, stateManager)), + factory.createIconButton(StandardActions.REDO, new UndoRedoAction(StandardActions.REDO, this, dialogService, stateManager)), + factory.createIconButton(StandardActions.CUT, new EditAction(StandardActions.CUT, this, stateManager)), + factory.createIconButton(StandardActions.COPY, new EditAction(StandardActions.COPY, this, stateManager)), + factory.createIconButton(StandardActions.PASTE, new EditAction(StandardActions.PASTE, this, stateManager)), new Separator(Orientation.VERTICAL), pushToApplicationButton, - factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new OldDatabaseCommandWrapper(Actions.MAKE_KEY, this, stateManager)), - factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new OldDatabaseCommandWrapper(Actions.CLEANUP, this, stateManager)), + factory.createIconButton(StandardActions.GENERATE_CITE_KEYS, new GenerateBibtexKeyAction(this, dialogService, stateManager)), + factory.createIconButton(StandardActions.CLEANUP_ENTRIES, new CleanupAction(this, prefs, dialogService, stateManager)), new Separator(Orientation.VERTICAL), factory.createIconButton(StandardActions.OPEN_GITHUB, new OpenBrowserAction("https://github.com/JabRef/jabref")), factory.createIconButton(StandardActions.OPEN_FACEBOOK, new OpenBrowserAction("https://www.facebook.com/JabRef/")), @@ -571,6 +573,9 @@ public void init() { } }); + // Wait for the scene to be created, otherwise focusOwnerProperty is not provided + Platform.runLater(() -> stateManager.focusOwnerProperty().bind( + EasyBind.map(mainStage.getScene().focusOwnerProperty(), Optional::ofNullable))); /* * The following state listener makes sure focus is registered with the * correct database when the user switches tabs. Without this, @@ -578,6 +583,7 @@ public void init() { */ EasyBind.subscribe(tabbedPane.getSelectionModel().selectedItemProperty(), tab -> { if (tab == null) { + stateManager.setSelectedEntries(Collections.emptyList()); return; } @@ -665,27 +671,27 @@ private MenuBar createMenu() { factory.createMenuItem(StandardActions.OPEN_LIBRARY, getOpenDatabaseAction()), fileHistory, - factory.createMenuItem(StandardActions.SAVE_LIBRARY, new OldDatabaseCommandWrapper(Actions.SAVE, this, stateManager)), - factory.createMenuItem(StandardActions.SAVE_LIBRARY_AS, new OldDatabaseCommandWrapper(Actions.SAVE_AS, this, stateManager)), + factory.createMenuItem(StandardActions.SAVE_LIBRARY, new SaveAction(SaveAction.SaveMethod.SAVE, this, stateManager)), + factory.createMenuItem(StandardActions.SAVE_LIBRARY_AS, new SaveAction(SaveAction.SaveMethod.SAVE_AS, this, stateManager)), factory.createMenuItem(StandardActions.SAVE_ALL, new SaveAllAction(this)), new SeparatorMenuItem(), factory.createSubMenu(StandardActions.IMPORT, - factory.createMenuItem(StandardActions.MERGE_DATABASE, new OldDatabaseCommandWrapper(Actions.MERGE_DATABASE, this, stateManager)), // TODO: merge with import + factory.createMenuItem(StandardActions.MERGE_DATABASE, new AppendDatabaseAction(this, dialogService, stateManager)), // TODO: merge with import factory.createMenuItem(StandardActions.IMPORT_INTO_CURRENT_LIBRARY, new ImportCommand(this, false, stateManager)), factory.createMenuItem(StandardActions.IMPORT_INTO_NEW_LIBRARY, new ImportCommand(this, true, stateManager))), factory.createSubMenu(StandardActions.EXPORT, factory.createMenuItem(StandardActions.EXPORT_ALL, new ExportCommand(this, false, Globals.prefs)), factory.createMenuItem(StandardActions.EXPORT_SELECTED, new ExportCommand(this, true, Globals.prefs)), - factory.createMenuItem(StandardActions.SAVE_SELECTED_AS_PLAIN_BIBTEX, new OldDatabaseCommandWrapper(Actions.SAVE_SELECTED_AS_PLAIN, this, stateManager))), + factory.createMenuItem(StandardActions.SAVE_SELECTED_AS_PLAIN_BIBTEX, new SaveAction(SaveAction.SaveMethod.SAVE_SELECTED, this, stateManager))), new SeparatorMenuItem(), factory.createSubMenu(StandardActions.REMOTE_DB, factory.createMenuItem(StandardActions.CONNECT_TO_SHARED_DB, new ConnectToSharedDatabaseCommand(this)), - factory.createMenuItem(StandardActions.PULL_CHANGES_FROM_SHARED_DB, new OldDatabaseCommandWrapper(Actions.PULL_CHANGES_FROM_SHARED_DATABASE, this, stateManager)) + factory.createMenuItem(StandardActions.PULL_CHANGES_FROM_SHARED_DB, new PullChangesFromSharedAction(stateManager)) ), new SeparatorMenuItem(), @@ -695,44 +701,47 @@ private MenuBar createMenu() { ); edit.getItems().addAll( - factory.createMenuItem(StandardActions.UNDO, new OldDatabaseCommandWrapper(Actions.UNDO, this, stateManager)), - factory.createMenuItem(StandardActions.REDO, new OldDatabaseCommandWrapper(Actions.REDO, this, stateManager)), + factory.createMenuItem(StandardActions.UNDO, new UndoRedoAction(StandardActions.UNDO, this, dialogService, stateManager)), + factory.createMenuItem(StandardActions.REDO, new UndoRedoAction(StandardActions.REDO, this, dialogService, stateManager)), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.CUT, new EditAction(Actions.CUT)), + factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, this, stateManager)), - factory.createMenuItem(StandardActions.COPY, new EditAction(Actions.COPY)), + factory.createMenuItem(StandardActions.COPY, new EditAction(StandardActions.COPY, this, stateManager)), factory.createSubMenu(StandardActions.COPY_MORE, - factory.createMenuItem(StandardActions.COPY_TITLE, new OldDatabaseCommandWrapper(Actions.COPY_TITLE, this, stateManager)), - factory.createMenuItem(StandardActions.COPY_KEY, new OldDatabaseCommandWrapper(Actions.COPY_KEY, this, stateManager)), - factory.createMenuItem(StandardActions.COPY_CITE_KEY, new OldDatabaseCommandWrapper(Actions.COPY_CITE_KEY, this, stateManager)), - factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new OldDatabaseCommandWrapper(Actions.COPY_KEY_AND_TITLE, this, stateManager)), - factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new OldDatabaseCommandWrapper(Actions.COPY_KEY_AND_LINK, this, stateManager)), - factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new OldDatabaseCommandWrapper(Actions.COPY_CITATION_HTML, this, stateManager)), + factory.createMenuItem(StandardActions.COPY_TITLE, new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, Globals.clipboardManager, prefs)), + factory.createMenuItem(StandardActions.COPY_KEY, new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, Globals.clipboardManager, prefs)), + factory.createMenuItem(StandardActions.COPY_CITE_KEY, new CopyMoreAction(StandardActions.COPY_CITE_KEY, dialogService, stateManager, Globals.clipboardManager, prefs)), + factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new CopyMoreAction(StandardActions.COPY_KEY_AND_TITLE, dialogService, stateManager, Globals.clipboardManager, prefs)), + factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new CopyMoreAction(StandardActions.COPY_KEY_AND_LINK, dialogService, stateManager, Globals.clipboardManager, prefs)), + factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.clipboardManager, prefs.getPreviewPreferences())), factory.createMenuItem(StandardActions.EXPORT_SELECTED_TO_CLIPBOARD, new ExportToClipboardAction(this, dialogService))), - factory.createMenuItem(StandardActions.PASTE, new EditAction(Actions.PASTE)), + factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, this, stateManager)), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.REPLACE_ALL, new OldDatabaseCommandWrapper(Actions.REPLACE_ALL, this, stateManager)), - factory.createMenuItem(StandardActions.GENERATE_CITE_KEYS, new OldDatabaseCommandWrapper(Actions.MAKE_KEY, this, stateManager)), + factory.createMenuItem(StandardActions.REPLACE_ALL, new ReplaceStringAction(this, stateManager)), + factory.createMenuItem(StandardActions.GENERATE_CITE_KEYS, new GenerateBibtexKeyAction(this, dialogService, stateManager)), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.MANAGE_KEYWORDS, new ManageKeywordsAction(stateManager)) + factory.createMenuItem(StandardActions.MANAGE_KEYWORDS, new ManageKeywordsAction(stateManager)), + factory.createMenuItem(StandardActions.MASS_SET_FIELDS, new MassSetFieldsAction(stateManager, dialogService, undoManager)) ); if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) { edit.getItems().addAll( new SeparatorMenuItem(), - SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.RANKING, factory, undoManager), - SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.RELEVANCE, factory), - SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.QUALITY, factory), - SpecialFieldMenuItemFactory.getSpecialFieldSingleItemForActiveDatabase(SpecialField.PRINTED, factory), - SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.PRIORITY, factory, undoManager), - SpecialFieldMenuItemFactory.createSpecialFieldMenuForActiveDatabase(SpecialField.READ_STATUS, factory, undoManager) + // ToDo: SpecialField needs the active BasePanel to mark it as changed. + // Refactor BasePanel, should mark the BibDatabaseContext or the UndoManager as dirty instead! + SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, this, dialogService, stateManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, this, dialogService, stateManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, this, dialogService, stateManager), + SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, this, dialogService, stateManager), + SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, this, dialogService, stateManager), + SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, this, dialogService, stateManager) ); } @@ -740,27 +749,21 @@ private MenuBar createMenu() { library.getItems().addAll( factory.createMenuItem(StandardActions.NEW_ENTRY, new NewEntryAction(this, dialogService, Globals.prefs, stateManager)), factory.createMenuItem(StandardActions.NEW_ENTRY_FROM_PLAIN_TEXT, new ExtractBibtexAction(stateManager)), - factory.createMenuItem(StandardActions.DELETE_ENTRY, new OldDatabaseCommandWrapper(Actions.DELETE, this, stateManager)), + factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, this, stateManager)), new SeparatorMenuItem(), factory.createMenuItem(StandardActions.LIBRARY_PROPERTIES, new LibraryPropertiesAction(this, dialogService, stateManager)), factory.createMenuItem(StandardActions.EDIT_PREAMBLE, new PreambleEditor(stateManager, undoManager, this.getDialogService())), factory.createMenuItem(StandardActions.EDIT_STRINGS, new BibtexStringEditorAction(stateManager)), - factory.createMenuItem(StandardActions.MANAGE_CITE_KEY_PATTERNS, new BibtexKeyPatternAction(this, stateManager)), - factory.createMenuItem(StandardActions.MASS_SET_FIELDS, new MassSetFieldsAction(stateManager, dialogService, undoManager)) + factory.createMenuItem(StandardActions.MANAGE_CITE_KEY_PATTERNS, new BibtexKeyPatternAction(this, stateManager)) ); - Menu lookupIdentifiers = factory.createSubMenu(StandardActions.LOOKUP_DOC_IDENTIFIER); - for (IdFetcher fetcher : WebFetchers.getIdFetchers(Globals.prefs.getImportFormatPreferences())) { - LookupIdentifierAction identifierAction = new LookupIdentifierAction<>(this, fetcher, stateManager, undoManager); - lookupIdentifiers.getItems().add(factory.createMenuItem(identifierAction.getAction(), identifierAction)); - } - quality.getItems().addAll( factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this, dialogService, stateManager)), + factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(this, dialogService, stateManager)), factory.createMenuItem(StandardActions.CHECK_INTEGRITY, new IntegrityCheckAction(this, stateManager, Globals.TASK_EXECUTOR)), - factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new OldDatabaseCommandWrapper(Actions.CLEANUP, this, stateManager)), + factory.createMenuItem(StandardActions.CLEANUP_ENTRIES, new CleanupAction(this, this.prefs, dialogService, stateManager)), new SeparatorMenuItem(), @@ -769,15 +772,26 @@ private MenuBar createMenu() { new SeparatorMenuItem(), factory.createSubMenu(StandardActions.ABBREVIATE, - factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new OldDatabaseCommandWrapper(Actions.ABBREVIATE_DEFAULT, this, stateManager)), - factory.createMenuItem(StandardActions.ABBREVIATE_MEDLINE, new OldDatabaseCommandWrapper(Actions.ABBREVIATE_MEDLINE, this, stateManager)), - factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new OldDatabaseCommandWrapper(Actions.ABBREVIATE_SHORTEST_UNIQUE, this, stateManager))), + factory.createMenuItem(StandardActions.ABBREVIATE_DEFAULT, new AbbreviateAction(StandardActions.ABBREVIATE_DEFAULT, this, dialogService, stateManager, prefs)), + factory.createMenuItem(StandardActions.ABBREVIATE_MEDLINE, new AbbreviateAction(StandardActions.ABBREVIATE_MEDLINE, this, dialogService, stateManager, prefs)), + factory.createMenuItem(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, new AbbreviateAction(StandardActions.ABBREVIATE_SHORTEST_UNIQUE, this, dialogService, stateManager, prefs))), - factory.createMenuItem(StandardActions.UNABBREVIATE, new OldDatabaseCommandWrapper(Actions.UNABBREVIATE, this, stateManager)) + factory.createMenuItem(StandardActions.UNABBREVIATE, new AbbreviateAction(StandardActions.UNABBREVIATE, this, dialogService, stateManager, prefs)) ); + Menu lookupIdentifiers = factory.createSubMenu(StandardActions.LOOKUP_DOC_IDENTIFIER); + for (IdFetcher fetcher : WebFetchers.getIdFetchers(Globals.prefs.getImportFormatPreferences())) { + LookupIdentifierAction identifierAction = new LookupIdentifierAction<>(this, fetcher, stateManager, undoManager); + lookupIdentifiers.getItems().add(factory.createMenuItem(identifierAction.getAction(), identifierAction)); + } + lookup.getItems().addAll( - factory.createMenuItem(StandardActions.FIND_DUPLICATES, new DuplicateSearch(this, dialogService, stateManager)) + lookupIdentifiers, + factory.createMenuItem(StandardActions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(dialogService, stateManager, prefs)), + + new SeparatorMenuItem(), + + factory.createMenuItem(StandardActions.FIND_UNLINKED_FILES, new FindUnlinkedFilesAction(this, stateManager)) ); // PushToApplication @@ -786,23 +800,17 @@ private MenuBar createMenu() { pushToApplicationsManager.setMenuItem(pushToApplicationMenuItem); tools.getItems().addAll( - factory.createMenuItem(StandardActions.PARSE_TEX, new ParseTexAction(stateManager)), + factory.createMenuItem(StandardActions.PARSE_LATEX, new ParseLatexAction(stateManager)), factory.createMenuItem(StandardActions.NEW_SUB_LIBRARY_FROM_AUX, new NewSubLibraryAction(this, stateManager)), new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.FIND_UNLINKED_FILES, new FindUnlinkedFilesAction(this, stateManager)), - factory.createMenuItem(StandardActions.WRITE_XMP, new OldDatabaseCommandWrapper(Actions.WRITE_XMP, this, stateManager)), + factory.createMenuItem(StandardActions.WRITE_XMP, new WriteXMPAction(stateManager, dialogService)), factory.createMenuItem(StandardActions.COPY_LINKED_FILES, new CopyFilesAction(stateManager, this.getDialogService())), new SeparatorMenuItem(), - lookupIdentifiers, - factory.createMenuItem(StandardActions.DOWNLOAD_FULL_TEXT, new OldDatabaseCommandWrapper(Actions.DOWNLOAD_FULL_TEXT, this, stateManager)), - - new SeparatorMenuItem(), - - factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new OldDatabaseCommandWrapper(Actions.SEND_AS_EMAIL, this, stateManager)), + factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsEMailAction(dialogService, stateManager)), pushToApplicationMenuItem ); @@ -820,14 +828,14 @@ private MenuBar createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.NEXT_PREVIEW_STYLE, new OldDatabaseCommandWrapper(Actions.NEXT_PREVIEW_STYLE, this, stateManager)), - factory.createMenuItem(StandardActions.PREVIOUS_PREVIEW_STYLE, new OldDatabaseCommandWrapper(Actions.PREVIOUS_PREVIEW_STYLE, this, stateManager)), + factory.createMenuItem(StandardActions.NEXT_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.NEXT, this, stateManager)), + factory.createMenuItem(StandardActions.PREVIOUS_PREVIEW_STYLE, new PreviewSwitchAction(PreviewSwitchAction.Direction.PREVIOUS, this, stateManager)), new SeparatorMenuItem(), factory.createMenuItem(StandardActions.SHOW_PDF_VIEWER, new ShowDocumentViewerAction()), - factory.createMenuItem(StandardActions.EDIT_ENTRY, new OldDatabaseCommandWrapper(Actions.EDIT, this, stateManager)), - factory.createMenuItem(StandardActions.OPEN_CONSOLE, new OldDatabaseCommandWrapper(Actions.OPEN_CONSOLE, this, stateManager)) + factory.createMenuItem(StandardActions.EDIT_ENTRY, new OpenEntryEditorAction(this, stateManager)), + factory.createMenuItem(StandardActions.OPEN_CONSOLE, new OpenConsoleAction(stateManager)) ); }); @@ -885,6 +893,7 @@ private MenuBar createMenu() { edit, library, quality, + lookup, tools, view, options, @@ -943,15 +952,11 @@ private List collectDatabaseFilePaths() { List dbPaths = new ArrayList<>(getBasePanelCount()); for (BasePanel basePanel : getBasePanelList()) { - try { - // db file exists - if (basePanel.getBibDatabaseContext().getDatabaseFile().isPresent()) { - dbPaths.add(basePanel.getBibDatabaseContext().getDatabaseFile().get().getCanonicalPath()); - } else { - dbPaths.add(""); - } - } catch (IOException ex) { - LOGGER.error("Invalid database file path: " + ex.getMessage()); + // db file exists + if (basePanel.getBibDatabaseContext().getDatabasePath().isPresent()) { + dbPaths.add(basePanel.getBibDatabaseContext().getDatabasePath().get().toAbsolutePath().toString()); + } else { + dbPaths.add(""); } } return dbPaths; @@ -967,10 +972,10 @@ public void updateAllTabTitles() { List paths = getUniquePathParts(); for (int i = 0; i < getBasePanelCount(); i++) { String uniqPath = paths.get(i); - Optional file = getBasePanelAt(i).getBibDatabaseContext().getDatabaseFile(); + Optional file = getBasePanelAt(i).getBibDatabaseContext().getDatabasePath(); if (file.isPresent()) { - if (!uniqPath.equals(file.get().getName()) && uniqPath.contains(File.separator)) { + if (!uniqPath.equals(file.get().getFileName()) && uniqPath.contains(File.separator)) { // remove filename uniqPath = uniqPath.substring(0, uniqPath.lastIndexOf(File.separator)); tabbedPane.getTabs().get(i).setText(getBasePanelAt(i).getTabTitle() + " \u2014 " + uniqPath); @@ -981,7 +986,7 @@ public void updateAllTabTitles() { } else { tabbedPane.getTabs().get(i).setText(getBasePanelAt(i).getTabTitle()); } - tabbedPane.getTabs().get(i).setTooltip(new Tooltip(file.map(File::getAbsolutePath).orElse(null))); + tabbedPane.getTabs().get(i).setTooltip(new Tooltip(file.map(Path::toAbsolutePath).map(Path::toString).orElse(null))); } } @@ -1008,7 +1013,7 @@ public void addTab(BasePanel basePanel, boolean raisePanel) { if (readyForAutosave(context)) { AutosaveManager autosaver = AutosaveManager.start(context); - autosaver.registerListener(new AutosaveUIManager(basePanel)); + autosaver.registerListener(new AutosaveUiManager(basePanel)); } BackupManager.start(context, Globals.entryTypesManager, prefs); @@ -1037,7 +1042,7 @@ private boolean readyForAutosave(BibDatabaseContext context) { return ((context.getLocation() == DatabaseLocation.SHARED) || ((context.getLocation() == DatabaseLocation.LOCAL) && Globals.prefs.getBoolean(JabRefPreferences.LOCAL_AUTO_SAVE))) && - context.getDatabaseFile().isPresent(); + context.getDatabasePath().isPresent(); } /** @@ -1112,7 +1117,7 @@ private boolean confirmClose(BasePanel panel) { .getDatabasePath() .map(Path::toAbsolutePath) .map(Path::toString) - .orElse(GUIGlobals.UNTITLED_TITLE); + .orElse(Localization.lang("untitled")); ButtonType saveChanges = new ButtonType(Localization.lang("Save changes"), ButtonBar.ButtonData.YES); ButtonType discardChanges = new ButtonType(Localization.lang("Discard changes"), ButtonBar.ButtonData.NO); @@ -1128,7 +1133,6 @@ private boolean confirmClose(BasePanel panel) { try { SaveDatabaseAction saveAction = new SaveDatabaseAction(panel, Globals.prefs, Globals.entryTypesManager); if (saveAction.save()) { - // Saved, now exit. return true; } // The action was either canceled or unsuccessful. @@ -1140,7 +1144,7 @@ private boolean confirmClose(BasePanel panel) { // Save was cancelled or an error occurred. return false; } - return !response.isPresent() || !response.get().equals(cancel); + return response.isEmpty() || !response.get().equals(cancel); } private void closeTab(BasePanel panel) { @@ -1207,34 +1211,6 @@ public DialogService getDialogService() { return dialogService; } - private void setDefaultTableFontSize() { - GUIGlobals.setFont(Globals.prefs.getIntDefault(JabRefPreferences.FONT_SIZE)); - for (BasePanel basePanel : getBasePanelList()) { - basePanel.updateTableFont(); - } - dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); - } - - private void increaseTableFontSize() { - GUIGlobals.setFont(GUIGlobals.currentFont.getSize() + 1); - for (BasePanel basePanel : getBasePanelList()) { - basePanel.updateTableFont(); - } - dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); - } - - private void decreaseTableFontSize() { - double currentSize = GUIGlobals.currentFont.getSize(); - if (currentSize < 2) { - return; - } - GUIGlobals.setFont(currentSize - 1); - for (BasePanel basePanel : getBasePanelList()) { - basePanel.updateTableFont(); - } - dialogService.notify(Localization.lang("Table font size is %0", String.valueOf(GUIGlobals.currentFont.getSize()))); - } - /** * The action concerned with closing the window. */ @@ -1246,63 +1222,6 @@ public void execute() { } } - /** - * Class for handling general actions; cut, copy and paste. The focused component is kept track of by - * Globals.focusListener, and we call the action stored under the relevant name in its action map. - */ - private class EditAction extends SimpleCommand { - - private final Actions command; - - public EditAction(Actions command) { - this.command = command; - } - - @Override - public String toString() { - return this.command.toString(); - } - - @Override - public void execute() { - Node focusOwner = mainStage.getScene().getFocusOwner(); - if (focusOwner != null) { - if (focusOwner instanceof TextInputControl) { - // Focus is on text field -> copy/paste/cut selected text - TextInputControl textInput = (TextInputControl) focusOwner; - switch (command) { - case COPY: - textInput.copy(); - break; - case CUT: - textInput.cut(); - break; - case PASTE: - // handled by FX in TextInputControl#paste - break; - default: - throw new IllegalStateException("Only cut/copy/paste supported but got " + command); - } - } else { - // Not sure what is selected -> copy/paste/cut selected entries - switch (command) { - case COPY: - getCurrentBasePanel().copy(); - break; - case CUT: - getCurrentBasePanel().cut(); - break; - case PASTE: - // handled by FX in TextInputControl#paste - break; - default: - throw new IllegalStateException("Only cut/copy/paste supported but got " + command); - } - } - } - } - } - private class CloseDatabaseAction extends SimpleCommand { @Override diff --git a/src/main/java/org/jabref/gui/OpenConsoleAction.java b/src/main/java/org/jabref/gui/OpenConsoleAction.java new file mode 100644 index 00000000000..ce634dd73e5 --- /dev/null +++ b/src/main/java/org/jabref/gui/OpenConsoleAction.java @@ -0,0 +1,34 @@ +package org.jabref.gui; + +import java.io.IOException; + +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.desktop.JabRefDesktop; +import org.jabref.model.database.BibDatabaseContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OpenConsoleAction extends SimpleCommand { + + private static final Logger LOGGER = LoggerFactory.getLogger(OpenConsoleAction.class); + private final StateManager stateManager; + + public OpenConsoleAction(StateManager stateManager) { + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsDatabase(stateManager)); + } + + @Override + public void execute() { + stateManager.getActiveDatabase().flatMap(BibDatabaseContext::getDatabasePath).ifPresent(path -> { + try { + JabRefDesktop.openConsole(path.toFile()); + } catch (IOException e) { + LOGGER.info("Could not open console", e); + } + }); + } +} diff --git a/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java b/src/main/java/org/jabref/gui/SendAsEMailAction.java similarity index 65% rename from src/main/java/org/jabref/gui/worker/SendAsEMailAction.java rename to src/main/java/org/jabref/gui/SendAsEMailAction.java index 24877673394..08f412b6a28 100644 --- a/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java +++ b/src/main/java/org/jabref/gui/SendAsEMailAction.java @@ -1,4 +1,4 @@ -package org.jabref.gui.worker; +package org.jabref.gui; import java.awt.Desktop; import java.io.IOException; @@ -9,15 +9,15 @@ import java.util.List; import org.jabref.Globals; -import org.jabref.gui.BasePanel; -import org.jabref.gui.JabRefFrame; -import org.jabref.gui.actions.BaseAction; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.desktop.JabRefDesktop; import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.bibtex.BibEntryWriter; import org.jabref.logic.bibtex.FieldWriter; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.io.FileUtil; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.preferences.JabRefPreferences; @@ -34,49 +34,50 @@ * are opened. This feature is disabled by default and can be switched on at * preferences/external programs */ -public class SendAsEMailAction implements BaseAction { +public class SendAsEMailAction extends SimpleCommand { private static final Logger LOGGER = LoggerFactory.getLogger(SendAsEMailAction.class); - private final JabRefFrame frame; + private DialogService dialogService; + private StateManager stateManager; - public SendAsEMailAction(JabRefFrame frame) { - this.frame = frame; + public SendAsEMailAction(DialogService dialogService, StateManager stateManager) { + this.dialogService = dialogService; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @Override - public void action() { + public void execute() { BackgroundTask.wrap(this::sendEmail) - .onSuccess(frame.getDialogService()::notify) + .onSuccess(dialogService::notify) .onFailure(e -> { String message = Localization.lang("Error creating email"); LOGGER.warn(message, e); - frame.getDialogService().notify(message); + dialogService.notify(message); }) .executeWith(Globals.TASK_EXECUTOR); } private String sendEmail() throws Exception { - if (!Desktop.isDesktopSupported()) { + if (!Desktop.isDesktopSupported() || stateManager.getActiveDatabase().isEmpty()) { return Localization.lang("Error creating email"); } - BasePanel panel = frame.getCurrentBasePanel(); - if (panel == null) { - throw new IllegalStateException("Base panel is not available."); - } - if (panel.getSelectedEntries().isEmpty()) { + if (stateManager.getSelectedEntries().isEmpty()) { return Localization.lang("This operation requires one or more entries to be selected."); } - StringWriter sw = new StringWriter(); - List bes = panel.getSelectedEntries(); + StringWriter rawEntries = new StringWriter(); + BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get(); + List entries = stateManager.getSelectedEntries(); // write the entries using sw, which is used later to form the email content BibEntryWriter bibtexEntryWriter = new BibEntryWriter(new FieldWriter(Globals.prefs.getFieldWriterPreferences()), Globals.entryTypesManager); - for (BibEntry entry : bes) { + for (BibEntry entry : entries) { try { - bibtexEntryWriter.write(entry, sw, panel.getBibDatabaseContext().getMode()); + bibtexEntryWriter.write(entry, rawEntries, databaseContext.getMode()); } catch (IOException e) { LOGGER.warn("Problem creating BibTeX file for mailing.", e); } @@ -88,20 +89,19 @@ private String sendEmail() throws Exception { // the unofficial "mailto:attachment" property boolean openFolders = JabRefPreferences.getInstance().getBoolean(JabRefPreferences.OPEN_FOLDERS_OF_ATTACHED_FILES); - List fileList = FileUtil.getListOfLinkedFiles(bes, frame.getCurrentBasePanel().getBibDatabaseContext() - .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())); - for (Path f : fileList) { - attachments.add(f.toAbsolutePath().toString()); + List fileList = FileUtil.getListOfLinkedFiles(entries, databaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())); + for (Path path : fileList) { + attachments.add(path.toAbsolutePath().toString()); if (openFolders) { try { - JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath()); + JabRefDesktop.openFolderAndSelectFile(path.toAbsolutePath()); } catch (IOException e) { LOGGER.debug("Cannot open file", e); } } } - String mailTo = "?Body=".concat(sw.getBuffer().toString()); + String mailTo = "?Body=".concat(rawEntries.getBuffer().toString()); mailTo = mailTo.concat("&Subject="); mailTo = mailTo.concat(JabRefPreferences.getInstance().get(JabRefPreferences.EMAIL_SUBJECT)); for (String path : attachments) { @@ -114,6 +114,6 @@ private String sendEmail() throws Exception { Desktop desktop = Desktop.getDesktop(); desktop.mail(uriMailTo); - return String.format("%s: %d", Localization.lang("Entries added to an email"), bes.size()); + return String.format("%s: %d", Localization.lang("Entries added to an email"), entries.size()); } } diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index c09f97244f0..05a9364f65d 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -13,7 +13,9 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; +import javafx.scene.Node; +import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.logic.search.SearchQuery; import org.jabref.model.database.BibDatabaseContext; @@ -27,20 +29,27 @@ * - currently selected group * - active search * - active number of search results + * - focus owner */ public class StateManager { + private final CustomLocalDragboard localDragboard = new CustomLocalDragboard(); private final OptionalObjectProperty activeDatabase = OptionalObjectProperty.empty(); private final ReadOnlyListWrapper activeGroups = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); private final ObservableList selectedEntries = FXCollections.observableArrayList(); private final ObservableMap> selectedGroups = FXCollections.observableHashMap(); private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); + private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); } + public CustomLocalDragboard getLocalDragboard() { + return localDragboard; + } + public OptionalObjectProperty activeDatabaseProperty() { return activeDatabase; } @@ -99,4 +108,8 @@ public void clearSearchQuery() { public void setSearchQuery(SearchQuery searchQuery) { activeSearchQuery.setValue(Optional.of(searchQuery)); } + + public OptionalObjectProperty focusOwnerProperty() { return focusOwner; } + + public Optional getFocusOwner() { return focusOwner.get(); } } diff --git a/src/main/java/org/jabref/gui/actions/ActionHelper.java b/src/main/java/org/jabref/gui/actions/ActionHelper.java index dfd66772a3a..01e23d67e22 100644 --- a/src/main/java/org/jabref/gui/actions/ActionHelper.java +++ b/src/main/java/org/jabref/gui/actions/ActionHelper.java @@ -1,9 +1,14 @@ package org.jabref.gui.actions; +import java.util.Collections; +import java.util.List; + import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanExpression; import org.jabref.gui.StateManager; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; public class ActionHelper { public static BooleanExpression needsDatabase(StateManager stateManager) { @@ -13,4 +18,22 @@ public static BooleanExpression needsDatabase(StateManager stateManager) { public static BooleanExpression needsEntriesSelected(StateManager stateManager) { return Bindings.isNotEmpty(stateManager.getSelectedEntries()); } + + public static BooleanExpression needsEntriesSelected(int numberOfEntries, StateManager stateManager) { + return Bindings.createBooleanBinding( + () -> stateManager.getSelectedEntries().size() == numberOfEntries, + stateManager.getSelectedEntries()); + } + + public static BooleanExpression isFieldSetForSelectedEntry(Field field, StateManager stateManager) { + return isAnyFieldSetForSelectedEntry(Collections.singletonList(field), stateManager); + } + + public static BooleanExpression isAnyFieldSetForSelectedEntry(List fields, StateManager stateManager) { + BibEntry entry = stateManager.getSelectedEntries().get(0); + return Bindings.createBooleanBinding( + () -> entry.getFields().stream().anyMatch(fields::contains), + entry.getFieldsObservable(), + stateManager.getSelectedEntries()); + } } diff --git a/src/main/java/org/jabref/gui/actions/Actions.java b/src/main/java/org/jabref/gui/actions/Actions.java deleted file mode 100644 index 49040b8e00b..00000000000 --- a/src/main/java/org/jabref/gui/actions/Actions.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.jabref.gui.actions; - -/** - * Global String constants for GUI actions - */ -@Deprecated -public enum Actions { - ABBREVIATE_DEFAULT, - ABBREVIATE_MEDLINE, - ABBREVIATE_SHORTEST_UNIQUE, - ADD_FILE_LINK, - CLEANUP, - COPY, - COPY_CITATION_ASCII_DOC, - COPY_CITATION_XSLFO, - COPY_CITATION_HTML, - COPY_CITATION_RTF, - COPY_CITATION_TEXT, - COPY_KEY, - COPY_CITE_KEY, - COPY_KEY_AND_TITLE, - COPY_KEY_AND_LINK, - COPY_TITLE, - CUT, - DELETE, - DOWNLOAD_FULL_TEXT, - EDIT, - EDIT_PREAMBLE, - EDIT_STRINGS, - EXPORT_TO_CLIPBOARD, - MAKE_KEY, - MANAGE_SELECTORS, - MERGE_DATABASE, - MERGE_ENTRIES, - MERGE_WITH_FETCHED_ENTRY, - NEXT_PREVIEW_STYLE, - OPEN_CONSOLE, - OPEN_EXTERNAL_FILE, - OPEN_FOLDER, - OPEN_URL, - PASTE, - PREVIOUS_PREVIEW_STYLE, - PULL_CHANGES_FROM_SHARED_DATABASE, - REDO, - REPLACE_ALL, - SAVE, - SAVE_AS, - SAVE_SELECTED_AS_PLAIN, - SELECT_ALL, - SEND_AS_EMAIL, - TOGGLE_GROUPS, - UNABBREVIATE, - UNDO, - WRITE_XMP, - PRINT_PREVIEW, - TOGGLE_PRINTED, - CLEAR_PRIORITY, - SET_PRIORITY_1, - SET_PRIORITY_2, - SET_PRIORITY_3, - TOGGLE_QUALITY_ASSURED, - CLEAR_RANK, - SET_RANK_1, - SET_RANK_2, - SET_RANK_3, - SET_RANK_4, - SET_RANK_5, - CLEAR_READ_STATUS, - SET_READ_STATUS_TO_READ, - SET_READ_STATUS_TO_SKIMMED, - TOGGLE_RELEVANCE -} diff --git a/src/main/java/org/jabref/gui/actions/BaseAction.java b/src/main/java/org/jabref/gui/actions/BaseAction.java deleted file mode 100644 index 0af963e1a30..00000000000 --- a/src/main/java/org/jabref/gui/actions/BaseAction.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.jabref.gui.actions; - -/** - * BaseAction is used to define actions that are called from the - * base frame through runCommand(). runCommand() finds the - * appropriate BaseAction object, and runs its action() method. - * - * @deprecated use {@link SimpleCommand} instead - */ -@FunctionalInterface -@Deprecated -public interface BaseAction { - - void action() throws Exception; -} diff --git a/src/main/java/org/jabref/gui/actions/JabRefAction.java b/src/main/java/org/jabref/gui/actions/JabRefAction.java index f04deb1157c..aba36dfe2e9 100644 --- a/src/main/java/org/jabref/gui/actions/JabRefAction.java +++ b/src/main/java/org/jabref/gui/actions/JabRefAction.java @@ -58,7 +58,11 @@ private String getActionName(Action action, Command command) { return action.getText(); } else { String commandName = command.getClass().getSimpleName(); - if ((command instanceof OldDatabaseCommandWrapper) || (command instanceof OldCommandWrapper) || commandName.contains("EditAction")) { + if ( commandName.contains("EditAction") + || commandName.contains("CopyMoreAction") + || commandName.contains("CopyCitationAction") + || commandName.contains("PreviewSwitchAction") + || commandName.contains("SaveAction")) { return command.toString(); } else { return commandName; diff --git a/src/main/java/org/jabref/gui/actions/OldCommandWrapper.java b/src/main/java/org/jabref/gui/actions/OldCommandWrapper.java deleted file mode 100644 index 028e0b153bb..00000000000 --- a/src/main/java/org/jabref/gui/actions/OldCommandWrapper.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.jabref.gui.actions; - -import javafx.beans.property.ReadOnlyDoubleProperty; - -import org.jabref.gui.BasePanel; -import org.jabref.gui.util.BindingsHelper; - -import de.saxsys.mvvmfx.utils.commands.CommandBase; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This wraps the old Swing commands so that they fit into the new infrastructure. - * In the long term, this class should be removed. - */ -@Deprecated -public class OldCommandWrapper extends CommandBase { - - private static final Logger LOGGER = LoggerFactory.getLogger(OldCommandWrapper.class); - - private final Actions command; - private final BasePanel panel; - - public OldCommandWrapper(Actions command, BasePanel panel) { - this.command = command; - this.panel = panel; - } - - @Override - public void execute() { - try { - panel.runCommand(command); - } catch (Throwable ex) { - LOGGER.debug("Cannot execute command " + command + ".", ex); - } - } - - @Override - public double getProgress() { - return 0; - } - - @Override - public ReadOnlyDoubleProperty progressProperty() { - return null; - } - - public void setExecutable(boolean executable) { - this.executable.bind(BindingsHelper.constantOf(executable)); - } - - @Override - public String toString() { - return this.command.toString(); - } -} diff --git a/src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java b/src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java deleted file mode 100644 index 23c2f6b86c9..00000000000 --- a/src/main/java/org/jabref/gui/actions/OldCommandWrapperForActiveDatabase.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.jabref.gui.actions; - -import javafx.beans.property.ReadOnlyDoubleProperty; - -import org.jabref.JabRefGUI; -import org.jabref.gui.util.BindingsHelper; - -import de.saxsys.mvvmfx.utils.commands.CommandBase; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This wraps the old Swing commands so that they fit into the new infrastructure. - * In the long term, this class should be removed. - */ -@Deprecated -public class OldCommandWrapperForActiveDatabase extends CommandBase { - - private static final Logger LOGGER = LoggerFactory.getLogger(OldCommandWrapperForActiveDatabase.class); - - private final Actions command; - - public OldCommandWrapperForActiveDatabase(Actions command) { - this.command = command; - } - - @Override - public void execute() { - try { - JabRefGUI.getMainFrame().getCurrentBasePanel().runCommand(command); - } catch (Throwable ex) { - LOGGER.debug("Cannot execute command " + command + ".", ex); - } - } - - @Override - public double getProgress() { - return 0; - } - - @Override - public ReadOnlyDoubleProperty progressProperty() { - return null; - } - - public void setExecutable(boolean executable) { - this.executable.bind(BindingsHelper.constantOf(executable)); - } -} diff --git a/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java b/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java deleted file mode 100644 index bcd8301d690..00000000000 --- a/src/main/java/org/jabref/gui/actions/OldDatabaseCommandWrapper.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.jabref.gui.actions; - -import java.util.Optional; - -import javafx.beans.property.ReadOnlyDoubleProperty; - -import org.jabref.gui.JabRefFrame; -import org.jabref.gui.StateManager; - -import de.saxsys.mvvmfx.utils.commands.CommandBase; -import org.fxmisc.easybind.EasyBind; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A command that is only executable if a database is open. - * Deprecated use instead - * @see org.jabref.gui.actions.SimpleCommand - */ -@Deprecated -public class OldDatabaseCommandWrapper extends CommandBase { - - private static final Logger LOGGER = LoggerFactory.getLogger(OldDatabaseCommandWrapper.class); - - private final Actions command; - private final JabRefFrame frame; - - public OldDatabaseCommandWrapper(Actions command, JabRefFrame frame, StateManager stateManager) { - this.command = command; - this.frame = frame; - - this.executable.bind( - EasyBind.map(stateManager.activeDatabaseProperty(), Optional::isPresent)); - } - - @Override - public void execute() { - if (frame.getTabbedPane().getTabs().size() > 0) { - try { - frame.getCurrentBasePanel().runCommand(command); - } catch (Throwable ex) { - LOGGER.error("Problem with executing command: " + command, ex); - } - } else { - LOGGER.info("Action '" + command + "' must be disabled when no database is open."); - } - } - - @Override - public double getProgress() { - return 0; - } - - @Override - public String toString() { - return this.command.toString(); - } - - @Override - public ReadOnlyDoubleProperty progressProperty() { - return null; - } -} diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 930d710a495..777f8113be9 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -33,6 +33,7 @@ public enum StandardActions implements Action { SEND_AS_EMAIL(Localization.lang("Send as email"), IconTheme.JabRefIcons.EMAIL), OPEN_EXTERNAL_FILE(Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE), OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI), + SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")), MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get BibTeX data from %0", "DOI/ISBN/...")), ATTACH_FILE(Localization.lang("Attach file"), IconTheme.JabRefIcons.ATTACH_FILE), PRIORITY(Localization.lang("Priority"), IconTheme.JabRefIcons.PRIORITY), @@ -86,7 +87,7 @@ public enum StandardActions implements Action { TOOGLE_OO(Localization.lang("OpenOffice/LibreOffice"), IconTheme.JabRefIcons.FILE_OPENOFFICE, KeyBinding.OPEN_OPEN_OFFICE_LIBRE_OFFICE_CONNECTION), TOGGLE_WEB_SEARCH(Localization.lang("Web search"), Localization.lang("Toggle web search interface"), IconTheme.JabRefIcons.WWW, KeyBinding.WEB_SEARCH), - PARSE_TEX(Localization.lang("Search for citations in LaTeX files..."), IconTheme.JabRefIcons.LATEX_CITATIONS), + PARSE_LATEX(Localization.lang("Search for citations in LaTeX files..."), IconTheme.JabRefIcons.LATEX_CITATIONS), NEW_SUB_LIBRARY_FROM_AUX(Localization.lang("New sublibrary based on AUX file") + "...", Localization.lang("New BibTeX sublibrary") + Localization.lang("This feature generates a new library based on which entries are needed in an existing LaTeX document."), IconTheme.JabRefIcons.NEW), WRITE_XMP(Localization.lang("Write XMP metadata to PDFs"), Localization.lang("Will write XMP metadata to the PDFs linked from selected entries."), KeyBinding.WRITE_XMP), OPEN_FOLDER(Localization.lang("Open folder"), Localization.lang("Open folder"), KeyBinding.OPEN_FOLDER), diff --git a/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeyAction.java b/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeyAction.java index d11de1046b7..d0697b31c6a 100644 --- a/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeyAction.java +++ b/src/main/java/org/jabref/gui/bibtexkeypattern/GenerateBibtexKeyAction.java @@ -3,9 +3,11 @@ import java.util.List; import org.jabref.Globals; -import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; -import org.jabref.gui.actions.BaseAction; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableKeyChange; import org.jabref.gui.util.BackgroundTask; @@ -14,27 +16,38 @@ import org.jabref.model.entry.BibEntry; import org.jabref.preferences.JabRefPreferences; -public class GenerateBibtexKeyAction implements BaseAction { +public class GenerateBibtexKeyAction extends SimpleCommand { + private final JabRefFrame frame; private final DialogService dialogService; - private final BasePanel basePanel; + private final StateManager stateManager; + private List entries; private boolean isCanceled; - public GenerateBibtexKeyAction(BasePanel basePanel, DialogService dialogService) { - this.basePanel = basePanel; + public GenerateBibtexKeyAction(JabRefFrame frame, DialogService dialogService, StateManager stateManager) { + this.frame = frame; this.dialogService = dialogService; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } - public void init() { - entries = basePanel.getSelectedEntries(); + @Override + public void execute() { + entries = stateManager.getSelectedEntries(); if (entries.isEmpty()) { dialogService.showWarningDialogAndWait(Localization.lang("Autogenerate BibTeX keys"), - Localization.lang("First select the entries you want keys to be generated for.")); + Localization.lang("First select the entries you want keys to be generated for.")); return; } dialogService.notify(formatOutputMessage(Localization.lang("Generating BibTeX key for"), entries.size())); + + checkOverwriteKeysChosen(); + + BackgroundTask.wrap(this::generateKeys) + .executeWith(Globals.TASK_EXECUTOR); } public static boolean confirmOverwriteKeys(DialogService dialogService) { @@ -73,34 +86,29 @@ private void generateKeys() { if (isCanceled) { return; } - // generate the new cite keys for each entry - final NamedCompound compound = new NamedCompound(Localization.lang("Autogenerate BibTeX keys")); - BibtexKeyGenerator keyGenerator = new BibtexKeyGenerator(basePanel.getBibDatabaseContext(), Globals.prefs.getBibtexKeyPatternPreferences()); - for (BibEntry entry : entries) { - keyGenerator.generateAndSetKey(entry) - .ifPresent(fieldChange -> compound.addEdit(new UndoableKeyChange(fieldChange))); - } - compound.end(); - // register the undo event only if new cite keys were generated - if (compound.hasEdits()) { - basePanel.getUndoManager().addEdit(compound); - } + stateManager.getActiveDatabase().ifPresent(databaseContext -> { + // generate the new cite keys for each entry + final NamedCompound compound = new NamedCompound(Localization.lang("Autogenerate BibTeX keys")); + BibtexKeyGenerator keyGenerator = new BibtexKeyGenerator(databaseContext, Globals.prefs.getBibtexKeyPatternPreferences()); + for (BibEntry entry : entries) { + keyGenerator.generateAndSetKey(entry) + .ifPresent(fieldChange -> compound.addEdit(new UndoableKeyChange(fieldChange))); + } + compound.end(); + + // register the undo event only if new cite keys were generated + if (compound.hasEdits()) { + frame.getUndoManager().addEdit(compound); + } - basePanel.markBaseChanged(); - dialogService.notify(formatOutputMessage(Localization.lang("Generated BibTeX key for"), entries.size())); + frame.getCurrentBasePanel().markBaseChanged(); + dialogService.notify(formatOutputMessage(Localization.lang("Generated BibTeX key for"), entries.size())); + }); } private String formatOutputMessage(String start, int count) { return String.format("%s %d %s.", start, count, (count > 1 ? Localization.lang("entries") : Localization.lang("entry"))); } - - @Override - public void action() { - init(); - checkOverwriteKeysChosen(); - BackgroundTask.wrap(this::generateKeys) - .executeWith(Globals.TASK_EXECUTOR); - } } diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java index ee47ea19ce4..8f71abc2c1a 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupAction.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupAction.java @@ -4,49 +4,66 @@ import java.util.Optional; import org.jabref.Globals; -import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; -import org.jabref.gui.actions.BaseAction; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.util.BackgroundTask; -import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.cleanup.CleanupPreset; import org.jabref.logic.cleanup.CleanupWorker; import org.jabref.logic.l10n.Localization; import org.jabref.model.FieldChange; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; -public class CleanupAction implements BaseAction { +public class CleanupAction extends SimpleCommand { - private final BasePanel panel; + private final JabRefFrame frame; + private final PreferencesService preferences; private final DialogService dialogService; - private final TaskExecutor taskExecutor; + private final StateManager stateManager; private boolean isCanceled; private int modifiedEntriesCount; - private final JabRefPreferences preferences; - public CleanupAction(BasePanel panel, JabRefPreferences preferences, TaskExecutor taskExecutor) { - this.panel = panel; + public CleanupAction(JabRefFrame frame, JabRefPreferences preferences, DialogService dialogService, StateManager stateManager) { + this.frame = frame; this.preferences = preferences; - this.dialogService = panel.frame().getDialogService(); - this.taskExecutor = taskExecutor; + this.dialogService = dialogService; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @Override - public void action() { - init(); - if (isCanceled) { + public void execute() { + if (stateManager.getActiveDatabase().isEmpty()) { return; } - CleanupDialog cleanupDialog = new CleanupDialog(panel.getBibDatabaseContext(), preferences.getCleanupPreset(), preferences.getFilePreferences()); - Optional chosenPreset = cleanupDialog.showAndWait(); + if (stateManager.getSelectedEntries().isEmpty()) { // None selected. Inform the user to select entries first. + dialogService.showInformationDialogAndWait(Localization.lang("Cleanup entry"), Localization.lang("First select entries to clean up.")); + return; + } + + dialogService.notify(Localization.lang("Doing a cleanup for %0 entries...", + Integer.toString(stateManager.getSelectedEntries().size()))); - if (chosenPreset.isPresent()) { - if (chosenPreset.get().isRenamePDFActive() && preferences.getBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN)) { + isCanceled = false; + modifiedEntriesCount = 0; + + Optional chosenPreset = new CleanupDialog( + stateManager.getActiveDatabase().get(), + preferences.getCleanupPreset(), + preferences.getFilePreferences()).showAndWait(); + + chosenPreset.ifPresent(preset -> { + if (preset.isRenamePDFActive() && Globals.prefs.getBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN)) { boolean confirmed = dialogService.showConfirmationDialogWithOptOutAndWait(Localization.lang("Autogenerate PDF Names"), Localization.lang("Auto-generating PDF-Names does not support undo. Continue?"), Localization.lang("Autogenerate PDF Names"), @@ -60,38 +77,24 @@ public void action() { } } - preferences.setCleanupPreset(chosenPreset.get()); + preferences.setCleanupPreset(preset); - BackgroundTask.wrap(() -> cleanup(chosenPreset.get())) + BackgroundTask.wrap(() -> cleanup(stateManager.getActiveDatabase().get(), preset)) .onSuccess(result -> showResults()) - .executeWith(taskExecutor); - } - } - - public void init() { - isCanceled = false; - modifiedEntriesCount = 0; - if (panel.getSelectedEntries().isEmpty()) { // None selected. Inform the user to select entries first. - dialogService.showInformationDialogAndWait(Localization.lang("Cleanup entry"), Localization.lang("First select entries to clean up.")); - isCanceled = true; - return; - } - dialogService.notify(Localization.lang("Doing a cleanup for %0 entries...", - Integer.toString(panel.getSelectedEntries().size()))); + .executeWith(Globals.TASK_EXECUTOR); + }); } /** * Runs the cleanup on the entry and records the change. */ - private void doCleanup(CleanupPreset preset, BibEntry entry, NamedCompound ce) { + private void doCleanup(BibDatabaseContext databaseContext, CleanupPreset preset, BibEntry entry, NamedCompound ce) { // Create and run cleaner - CleanupWorker cleaner = new CleanupWorker(panel.getBibDatabaseContext(), preferences.getCleanupPreferences( - Globals.journalAbbreviationLoader)); - List changes = cleaner.cleanup(preset, entry); + CleanupWorker cleaner = new CleanupWorker( + databaseContext, + preferences.getCleanupPreferences(Globals.journalAbbreviationLoader)); - if (changes.isEmpty()) { - return; - } + List changes = cleaner.cleanup(preset, entry); // Register undo action for (FieldChange change : changes) { @@ -105,37 +108,32 @@ private void showResults() { } if (modifiedEntriesCount > 0) { - panel.updateEntryEditorIfShowing(); - panel.markBaseChanged(); + frame.getCurrentBasePanel().updateEntryEditorIfShowing(); + frame.getCurrentBasePanel().markBaseChanged(); } - String message; - switch (modifiedEntriesCount) { - case 0: - message = Localization.lang("No entry needed a clean up"); - break; - case 1: - message = Localization.lang("One entry needed a clean up"); - break; - default: - message = Localization.lang("%0 entries needed a clean up", Integer.toString(modifiedEntriesCount)); - break; + + if (modifiedEntriesCount == 0) { + dialogService.notify(Localization.lang("No entry needed a clean up")); + } else if (modifiedEntriesCount == 1) { + dialogService.notify(Localization.lang("One entry needed a clean up")); + } else { + dialogService.notify(Localization.lang("%0 entries needed a clean up", Integer.toString(modifiedEntriesCount))); } - dialogService.notify(message); } - private void cleanup(CleanupPreset cleanupPreset) { + private void cleanup(BibDatabaseContext databaseContext, CleanupPreset cleanupPreset) { preferences.setCleanupPreset(cleanupPreset); - for (BibEntry entry : panel.getSelectedEntries()) { + for (BibEntry entry : stateManager.getSelectedEntries()) { // undo granularity is on entry level NamedCompound ce = new NamedCompound(Localization.lang("Cleanup entry")); - doCleanup(cleanupPreset, entry, ce); + doCleanup(databaseContext, cleanupPreset, entry, ce); ce.end(); if (ce.hasEdits()) { modifiedEntriesCount++; - panel.getUndoManager().addEdit(ce); + frame.getUndoManager().addEdit(ce); } } } diff --git a/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java b/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java index f75e08b9761..70cc748e2b7 100644 --- a/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java +++ b/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java @@ -106,6 +106,7 @@ private void buildLayout() { actionsList = new ListView<>(actions); actionsList.setMinHeight(100.0); actionsList.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); + new ViewModelListCellFactory() .withText(action -> action.getField().getDisplayName() + ": " + action.getFormatter().getName()) .withStringTooltip(action -> action.getFormatter().getDescription()) @@ -179,6 +180,7 @@ private GridPane getSelectorPanel() { .withStringTooltip(Formatter::getDescription) .install(formattersCombobox); EasyBind.subscribe(formattersCombobox.valueProperty(), e -> updateDescription()); + formattersCombobox.getSelectionModel().selectFirst(); builder.add(formattersCombobox, 3, 1); addButton = new Button(Localization.lang("Add")); diff --git a/src/main/java/org/jabref/gui/customjfx/CustomJFXPanel.java b/src/main/java/org/jabref/gui/customjfx/CustomJFXPanel.java deleted file mode 100644 index e21f8ce805e..00000000000 --- a/src/main/java/org/jabref/gui/customjfx/CustomJFXPanel.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jabref.gui.customjfx; - -import javafx.embed.swing.JFXPanel; -import javafx.scene.Scene; - -import org.jabref.Globals; -import org.jabref.gui.util.DefaultTaskExecutor; - -/** - * TODO: Remove as soon as possible - */ -public class CustomJFXPanel { - - public static JFXPanel wrap(Scene scene) { - JFXPanel container = new JFXPanel(); - Globals.getThemeLoader().installCss(scene, Globals.prefs); - DefaultTaskExecutor.runInJavaFXThread(() -> container.setScene(scene)); - return container; - } - -} diff --git a/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java b/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java index 4b7d1f66fda..457503ae272 100644 --- a/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/os/DefaultDesktop.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,11 +32,6 @@ public void openConsole(String absolutePath) throws IOException { LOGGER.error("This feature is not supported by your Operating System."); } - @Override - public void openPdfWithParameters(String filePath, List parameters) throws IOException { - //TODO imlement default - } - @Override public String detectProgramPath(String programName, String directoryName) { return programName; diff --git a/src/main/java/org/jabref/gui/desktop/os/Linux.java b/src/main/java/org/jabref/gui/desktop/os/Linux.java index 579b4986774..ff48658a5ff 100644 --- a/src/main/java/org/jabref/gui/desktop/os/Linux.java +++ b/src/main/java/org/jabref/gui/desktop/os/Linux.java @@ -6,23 +6,17 @@ import java.io.InputStreamReader; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import java.util.Locale; import java.util.Optional; -import java.util.StringJoiner; import org.jabref.JabRefExecutorService; import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.util.StreamGobbler; -import org.jabref.preferences.JabRefPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.jabref.preferences.JabRefPreferences.ADOBE_ACROBAT_COMMAND; -import static org.jabref.preferences.JabRefPreferences.USE_PDF_READER; - public class Linux implements NativeDesktop { private static final Logger LOGGER = LoggerFactory.getLogger(Linux.class); @@ -108,23 +102,6 @@ public void openConsole(String absolutePath) throws IOException { } } - @Override - public void openPdfWithParameters(String filePath, List parameters) throws IOException { - - String application; - if (JabRefPreferences.getInstance().get(USE_PDF_READER).equals(JabRefPreferences.getInstance().get(ADOBE_ACROBAT_COMMAND))) { - application = "acroread"; - - StringJoiner sj = new StringJoiner(" "); - sj.add(application); - parameters.forEach((param) -> sj.add(param)); - - openFileWithApplication(filePath, sj.toString()); - } else { - openFile(filePath, "PDF"); - } - } - @Override public String detectProgramPath(String programName, String directoryName) { return programName; diff --git a/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java b/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java index ed54cf78e0e..f4c2014384b 100644 --- a/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; public interface NativeDesktop { void openFile(String filePath, String fileType) throws IOException; @@ -21,14 +20,6 @@ public interface NativeDesktop { void openConsole(String absolutePath) throws IOException; - /** - * This method opens a pdf using the giving the parameters to the executing pdf reader - * @param filePath absolute path to the pdf file to be opened - * @param parameters console parameters depending on the pdf reader - * @throws IOException - */ - void openPdfWithParameters(String filePath, List parameters) throws IOException; - String detectProgramPath(String programName, String directoryName); /** diff --git a/src/main/java/org/jabref/gui/desktop/os/OSX.java b/src/main/java/org/jabref/gui/desktop/os/OSX.java index c4a6c495431..9abd006cdce 100644 --- a/src/main/java/org/jabref/gui/desktop/os/OSX.java +++ b/src/main/java/org/jabref/gui/desktop/os/OSX.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import java.util.Optional; import org.jabref.gui.externalfiletype.ExternalFileType; @@ -42,11 +41,6 @@ public void openConsole(String absolutePath) throws IOException { Runtime.getRuntime().exec("open -a Terminal " + absolutePath, null, new File(absolutePath)); } - @Override - public void openPdfWithParameters(String filePath, List parameters) throws IOException { - //TODO implement - } - @Override public String detectProgramPath(String programName, String directoryName) { return programName; diff --git a/src/main/java/org/jabref/gui/desktop/os/Windows.java b/src/main/java/org/jabref/gui/desktop/os/Windows.java index 5de0f056f65..00367d1b2c9 100644 --- a/src/main/java/org/jabref/gui/desktop/os/Windows.java +++ b/src/main/java/org/jabref/gui/desktop/os/Windows.java @@ -4,16 +4,10 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import java.util.Optional; import org.jabref.gui.externalfiletype.ExternalFileType; import org.jabref.gui.externalfiletype.ExternalFileTypes; -import org.jabref.preferences.JabRefPreferences; - -import static org.jabref.preferences.JabRefPreferences.ADOBE_ACROBAT_COMMAND; -import static org.jabref.preferences.JabRefPreferences.SUMATRA_PDF_COMMAND; -import static org.jabref.preferences.JabRefPreferences.USE_PDF_READER; public class Windows implements NativeDesktop { private static String DEFAULT_EXECUTABLE_EXTENSION = ".exe"; @@ -69,20 +63,4 @@ public void openConsole(String absolutePath) throws IOException { process.directory(new File(absolutePath)); process.start(); } - - @Override - public void openPdfWithParameters(String filePath, List parameters) throws IOException { - String pdfReaderPath = JabRefPreferences.getInstance().get(USE_PDF_READER); - if (pdfReaderPath.equals(SUMATRA_PDF_COMMAND) || pdfReaderPath.equals(ADOBE_ACROBAT_COMMAND)) { - String[] command = new String[parameters.size() + 2]; - command[0] = "\"" + Paths.get(pdfReaderPath).toString() + "\""; - for (int i = 1; i < command.length - 1; i++) { - command[i] = "\"" + parameters.get(i - 1) + "\""; - } - command[command.length - 1] = "\"" + filePath + "\""; - new ProcessBuilder(command).start(); - } else { - openFile(filePath, "PDF"); - } - } } diff --git a/src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java b/src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java similarity index 81% rename from src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java rename to src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java index c9268c40dc1..a1d6da59753 100644 --- a/src/main/java/org/jabref/gui/dialogs/AutosaveUIManager.java +++ b/src/main/java/org/jabref/gui/dialogs/AutosaveUiManager.java @@ -10,16 +10,15 @@ import org.slf4j.LoggerFactory; /** - * This class has an abstract UI role as it listens for an {@link AutosaveEvent} - * and saves the bib file associated with the given {@link BasePanel}. + * This class has an abstract UI role as it listens for an {@link AutosaveEvent} and saves the bib file associated with + * the given {@link BasePanel}. */ -public class AutosaveUIManager { +public class AutosaveUiManager { + private static final Logger LOGGER = LoggerFactory.getLogger(AutosaveUiManager.class); - private static final Logger LOGGER = LoggerFactory.getLogger(AutosaveUIManager.class); private final BasePanel panel; - - public AutosaveUIManager(BasePanel panel) { + public AutosaveUiManager(BasePanel panel) { this.panel = panel; } diff --git a/src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java b/src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java deleted file mode 100644 index 86e0912400a..00000000000 --- a/src/main/java/org/jabref/gui/edit/CopyBibTeXKeyAndLinkAction.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.jabref.gui.edit; - -import java.util.List; -import java.util.stream.Collectors; - -import org.jabref.JabRefGUI; -import org.jabref.gui.ClipBoardManager; -import org.jabref.gui.JabRefDialogService; -import org.jabref.gui.actions.BaseAction; -import org.jabref.gui.maintable.MainTable; -import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.logic.l10n.Localization; -import org.jabref.logic.util.OS; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.StandardField; - -/** - * This class will copy each selected entry's BibTeX key as a hyperlink to its url to the clipboard. - * In case an entry doesn't have a BibTeX key it will not be copied. - * In case an entry doesn't have an url this will only copy the BibTeX key. - */ -public class CopyBibTeXKeyAndLinkAction implements BaseAction { - - private final MainTable mainTable; - private final ClipBoardManager clipboardManager; - - public CopyBibTeXKeyAndLinkAction(MainTable mainTable, ClipBoardManager clipboardManager) { - this.mainTable = mainTable; - this.clipboardManager = clipboardManager; - } - - @Override - public void action() throws Exception { - List entries = mainTable.getSelectedEntries(); - if (!entries.isEmpty()) { - StringBuilder sb = new StringBuilder(); - - List entriesWithKey = entries.stream().filter(BibEntry::hasCiteKey).collect(Collectors.toList()); - - if (entriesWithKey.isEmpty()) { - JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("None of the selected entries have BibTeX keys.")); - return; - } - - for (BibEntry entry : entriesWithKey) { - String key = entry.getCiteKeyOptional().get(); - String url = entry.getField(StandardField.URL).orElse(""); - sb.append(url.isEmpty() ? key : String.format("%s", url, key)); - sb.append(OS.NEWLINE); - } - final String keyAndLink = sb.toString(); - DefaultTaskExecutor.runInJavaFXThread(() -> clipboardManager.setHtmlContent(keyAndLink)); - - int copied = entriesWithKey.size(); - int toCopy = entries.size(); - if (copied == toCopy) { - // All entries had keys. - JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Copied") + " '" + JabRefDialogService.shortenDialogMessage(keyAndLink) + "'."); - } else { - JabRefGUI.getMainFrame().getDialogService().notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", - Long.toString(toCopy - copied), Integer.toString(toCopy))); - } - } - } -} diff --git a/src/main/java/org/jabref/gui/edit/CopyMoreAction.java b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java new file mode 100644 index 00000000000..c2bb13170d0 --- /dev/null +++ b/src/main/java/org/jabref/gui/edit/CopyMoreAction.java @@ -0,0 +1,238 @@ +package org.jabref.gui.edit; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.jabref.Globals; +import org.jabref.gui.ClipBoardManager; +import org.jabref.gui.DialogService; +import org.jabref.gui.JabRefDialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.actions.StandardActions; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.layout.Layout; +import org.jabref.logic.layout.LayoutHelper; +import org.jabref.logic.util.OS; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CopyMoreAction extends SimpleCommand { + + private static final Logger LOGGER = LoggerFactory.getLogger(CopyMoreAction.class); + private final StandardActions action; + private final DialogService dialogService; + private final StateManager stateManager; + private final ClipBoardManager clipBoardManager; + private final PreferencesService preferencesService; + + public CopyMoreAction(StandardActions action, DialogService dialogService, StateManager stateManager, ClipBoardManager clipBoardManager, PreferencesService preferencesService) { + this.action = action; + this.dialogService = dialogService; + this.stateManager = stateManager; + this.clipBoardManager = clipBoardManager; + this.preferencesService = preferencesService; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); + } + + @Override + public void execute() { + if (stateManager.getActiveDatabase().isEmpty() || stateManager.getSelectedEntries().isEmpty()) { + return; + } + + switch (action) { + case COPY_TITLE: + copyTitle(); + break; + case COPY_KEY: + copyKey(); + break; + case COPY_CITE_KEY: + copyCiteKey(); + break; + case COPY_KEY_AND_TITLE: + copyKeyAndTitle(); + break; + case COPY_KEY_AND_LINK: + copyKeyAndLink(); + break; + default: + LOGGER.info("Unknown copy command."); + break; + } + } + + private void copyTitle() { + List selectedBibEntries = stateManager.getSelectedEntries(); + + List titles = selectedBibEntries.stream() + .filter(bibEntry -> bibEntry.getTitle().isPresent()) + .map(bibEntry -> bibEntry.getTitle().get()) + .collect(Collectors.toList()); + + if (titles.isEmpty()) { + dialogService.notify(Localization.lang("None of the selected entries have titles.")); + return; + } + + final String copiedTitles = String.join("\n", titles); + clipBoardManager.setContent(copiedTitles); + + if (titles.size() == selectedBibEntries.size()) { + // All entries had titles. + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedTitles))); + } else { + dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined title.", + Integer.toString(selectedBibEntries.size() - titles.size()), Integer.toString(selectedBibEntries.size()))); + } + } + + private void copyKey() { + List entries = stateManager.getSelectedEntries(); + + // Collect all non-null keys. + List keys = entries.stream() + .filter(entry -> entry.getCiteKeyOptional().isPresent()) + .map(entry -> entry.getCiteKeyOptional().get()) + .collect(Collectors.toList()); + + if (keys.isEmpty()) { + dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys.")); + return; + } + + final String copiedKeys = String.join(",", keys); + clipBoardManager.setContent(copiedKeys); + + if (keys.size() == entries.size()) { + // All entries had keys. + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedKeys))); + } else { + dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", + Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size()))); + } + } + + private void copyCiteKey() { + List entries = stateManager.getSelectedEntries(); + + // Collect all non-null keys. + List keys = entries.stream() + .filter(entry -> entry.getCiteKeyOptional().isPresent()) + .map(entry -> entry.getCiteKeyOptional().get()) + .collect(Collectors.toList()); + + if (keys.isEmpty()) { + dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys.")); + return; + } + + String citeCommand = Optional.ofNullable(Globals.prefs.get(JabRefPreferences.CITE_COMMAND)) + .filter(cite -> cite.contains("\\")) // must contain \ + .orElse("\\cite"); + + final String copiedCiteCommand = citeCommand + "{" + String.join(",", keys) + '}'; + clipBoardManager.setContent(copiedCiteCommand); + + if (keys.size() == entries.size()) { + // All entries had keys. + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(copiedCiteCommand))); + } else { + dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", + Integer.toString(entries.size() - keys.size()), Integer.toString(entries.size()))); + } + } + + private void copyKeyAndTitle() { + List entries = stateManager.getSelectedEntries(); + + // ToDo: this string should be configurable to allow arbitrary exports + StringReader layoutString = new StringReader("\\bibtexkey - \\begin{title}\\format[RemoveBrackets]{\\title}\\end{title}\n"); + Layout layout; + try { + layout = new LayoutHelper(layoutString, preferencesService.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader)).getLayoutFromText(); + } catch (IOException e) { + LOGGER.info("Could not get layout.", e); + return; + } + + StringBuilder keyAndTitle = new StringBuilder(); + + int entriesWithKeys = 0; + // Collect all non-null keys. + for (BibEntry entry : entries) { + if (entry.hasCiteKey()) { + entriesWithKeys++; + keyAndTitle.append(layout.doLayout(entry, stateManager.getActiveDatabase().get().getDatabase())); + } + } + + if (entriesWithKeys == 0) { + dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys.")); + return; + } + + clipBoardManager.setContent(keyAndTitle.toString()); + + if (entriesWithKeys == entries.size()) { + // All entries had keys. + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(keyAndTitle.toString()))); + } else { + dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", + Integer.toString(entries.size() - entriesWithKeys), Integer.toString(entries.size()))); + } + } + + /** + * This method will copy each selected entry's BibTeX key as a hyperlink to its url to the clipboard. In case an + * entry doesn't have a BibTeX key it will not be copied. In case an entry doesn't have an url this will only copy + * the BibTeX key. + */ + private void copyKeyAndLink() { + List entries = stateManager.getSelectedEntries(); + + StringBuilder keyAndLink = new StringBuilder(); + + List entriesWithKey = entries.stream() + .filter(BibEntry::hasCiteKey) + .collect(Collectors.toList()); + + if (entriesWithKey.isEmpty()) { + dialogService.notify(Localization.lang("None of the selected entries have BibTeX keys.")); + return; + } + + for (BibEntry entry : entriesWithKey) { + String key = entry.getCiteKeyOptional().get(); + String url = entry.getField(StandardField.URL).orElse(""); + keyAndLink.append(url.isEmpty() ? key : String.format("%s", url, key)); + keyAndLink.append(OS.NEWLINE); + } + + clipBoardManager.setHtmlContent(keyAndLink.toString()); + + if (entriesWithKey.size() == entries.size()) { + // All entries had keys. + dialogService.notify(Localization.lang("Copied '%0' to clipboard.", + JabRefDialogService.shortenDialogMessage(keyAndLink.toString()))); + } else { + dialogService.notify(Localization.lang("Warning: %0 out of %1 entries have undefined BibTeX key.", + Long.toString(entries.size() - entriesWithKey.size()), Integer.toString(entries.size()))); + } + } +} diff --git a/src/main/java/org/jabref/gui/edit/EditAction.java b/src/main/java/org/jabref/gui/edit/EditAction.java new file mode 100644 index 00000000000..e48add2b08c --- /dev/null +++ b/src/main/java/org/jabref/gui/edit/EditAction.java @@ -0,0 +1,76 @@ +package org.jabref.gui.edit; + +import javafx.scene.control.TextInputControl; + +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.actions.StandardActions; + +/** + * Class for handling general actions; cut, copy and paste. The focused component is kept track of by + * Globals.focusListener, and we call the action stored under the relevant name in its action map. + */ +public class EditAction extends SimpleCommand { + + private final JabRefFrame frame; + private final StandardActions action; + private final StateManager stateManager; + + public EditAction(StandardActions action, JabRefFrame frame, StateManager stateManager) { + this.action = action; + this.frame = frame; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); + } + + @Override + public String toString() { + return this.action.toString(); + } + + @Override + public void execute() { + stateManager.getFocusOwner().ifPresent(focusOwner -> { + if (focusOwner instanceof TextInputControl) { + // Focus is on text field -> copy/paste/cut selected text + TextInputControl textInput = (TextInputControl) focusOwner; + switch (action) { + case COPY: + textInput.copy(); + break; + case CUT: + textInput.cut(); + break; + case PASTE: + // handled by FX in TextInputControl#paste + break; + default: + throw new IllegalStateException("Only cut/copy/paste supported in TextInputControl but got " + action); + } + } else { + // Not sure what is selected -> copy/paste/cut selected entries + + // ToDo: Should be handled by BibDatabaseContext instead of BasePanel + switch (action) { + case COPY: + frame.getCurrentBasePanel().copy(); + break; + case CUT: + frame.getCurrentBasePanel().cut(); + break; + case PASTE: + // handled by FX in TextInputControl#paste + break; + case DELETE_ENTRY: + frame.getCurrentBasePanel().delete(false); + break; + default: + throw new IllegalStateException("Only cut/copy/paste supported but got " + action); + } + } + }); + } +} diff --git a/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java b/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java index ae759a01648..a903d812514 100644 --- a/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java +++ b/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java @@ -1,19 +1,23 @@ package org.jabref.gui.edit; -import org.jabref.gui.BasePanel; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; public class ReplaceStringAction extends SimpleCommand { - private BasePanel basePanel; + private final JabRefFrame frame; - public ReplaceStringAction(BasePanel basePanel) { - this.basePanel = basePanel; + public ReplaceStringAction(JabRefFrame frame, StateManager stateManager) { + this.frame = frame; + + this.executable.bind(ActionHelper.needsDatabase(stateManager)); } @Override public void execute() { - ReplaceStringView dialog = new ReplaceStringView(basePanel); + ReplaceStringView dialog = new ReplaceStringView(frame.getCurrentBasePanel()); dialog.showAndWait(); } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 18fe1f0fd00..f6cbfb7b996 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -28,7 +28,6 @@ import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; -import org.jabref.gui.GUIGlobals; import org.jabref.gui.StateManager; import org.jabref.gui.bibtexkeypattern.GenerateBibtexKeySingleAction; import org.jabref.gui.entryeditor.fileannotationtab.FileAnnotationTab; @@ -39,7 +38,6 @@ import org.jabref.gui.menus.ChangeEntryTypeMenu; import org.jabref.gui.mergeentries.FetchAndMergeEntry; import org.jabref.gui.undo.CountingUndoManager; -import org.jabref.gui.util.ColorUtil; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.TypedBibEntry; @@ -103,13 +101,6 @@ public EntryEditor(BasePanel panel, ExternalFileTypes externalFileTypes) { this.fileLinker = new ExternalFilesEntryLinker(externalFileTypes, preferencesService.getFilePreferences(), databaseContext); - if (GUIGlobals.currentFont != null) { - setStyle(String.format("text-area-background: %s;text-area-foreground: %s;text-area-highlight: %s;", - ColorUtil.toHex(GUIGlobals.validFieldBackgroundColor), - ColorUtil.toHex(GUIGlobals.editorTextColor), - ColorUtil.toHex(GUIGlobals.activeBackgroundColor))); - } - EasyBind.subscribe(tabbed.getSelectionModel().selectedItemProperty(), tab -> { EntryEditorTab activeTab = (EntryEditorTab) tab; if (activeTab != null) { diff --git a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java index 13546cbc64e..5bda2a4c3c0 100644 --- a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java +++ b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java @@ -26,11 +26,11 @@ import org.jabref.gui.util.DirectoryDialogConfiguration; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.texparser.DefaultTexParser; +import org.jabref.logic.texparser.DefaultLatexParser; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.texparser.Citation; -import org.jabref.model.texparser.TexParserResult; +import org.jabref.model.texparser.LatexParserResult; import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; @@ -56,7 +56,7 @@ enum Status { private final ObjectProperty status; private final StringProperty searchError; private Future searchTask; - private TexParserResult texParserResult; + private LatexParserResult latexParserResult; private BibEntry currentEntry; public LatexCitationsTabViewModel(BibDatabaseContext databaseContext, PreferencesService preferencesService, @@ -129,7 +129,7 @@ private Collection searchAndParse(String citeKey) throws IOException { Path newDirectory = databaseContext.getMetaData().getLatexFileDirectory(preferencesService.getUser()) .orElseGet(preferencesService::getWorkingDir); - if (texParserResult == null || !newDirectory.equals(directory.get())) { + if (latexParserResult == null || !newDirectory.equals(directory.get())) { directory.set(newDirectory); if (!newDirectory.toFile().exists()) { @@ -137,10 +137,10 @@ private Collection searchAndParse(String citeKey) throws IOException { } List texFiles = searchDirectory(newDirectory, new ArrayList<>()); - texParserResult = new DefaultTexParser().parse(texFiles); + latexParserResult = new DefaultLatexParser().parse(texFiles); } - return texParserResult.getCitationsByKey(citeKey); + return latexParserResult.getCitationsByKey(citeKey); } private List searchDirectory(Path directory, List texFiles) { diff --git a/src/main/java/org/jabref/gui/entryeditor/OpenEntryEditorAction.java b/src/main/java/org/jabref/gui/entryeditor/OpenEntryEditorAction.java new file mode 100644 index 00000000000..e6e759880e7 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/OpenEntryEditorAction.java @@ -0,0 +1,25 @@ +package org.jabref.gui.entryeditor; + +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; + +public class OpenEntryEditorAction extends SimpleCommand { + + private final JabRefFrame frame; + private final StateManager stateManager; + + public OpenEntryEditorAction(JabRefFrame frame, StateManager stateManager) { + this.frame = frame; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); + } + + public void execute() { + if (!stateManager.getSelectedEntries().isEmpty()) { + frame.getCurrentBasePanel().showAndEdit(stateManager.getSelectedEntries().get(0)); + } + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java b/src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java new file mode 100644 index 00000000000..03247c4fdd0 --- /dev/null +++ b/src/main/java/org/jabref/gui/entryeditor/PreviewSwitchAction.java @@ -0,0 +1,31 @@ +package org.jabref.gui.entryeditor; + +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.SimpleCommand; + +import static org.jabref.gui.actions.ActionHelper.needsDatabase; + +public class PreviewSwitchAction extends SimpleCommand { + + public enum Direction { PREVIOUS, NEXT } + + private final JabRefFrame frame; + private final Direction direction; + + public PreviewSwitchAction(Direction direction, JabRefFrame frame, StateManager stateManager) { + this.frame = frame; + this.direction = direction; + + this.executable.bind(needsDatabase(stateManager)); + } + + @Override + public void execute() { + if (direction == Direction.NEXT) { + frame.getCurrentBasePanel().getEntryEditor().nextPreviewStyle(); + } else { + frame.getCurrentBasePanel().getEntryEditor().previousPreviewStyle(); + } + } +} diff --git a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java index d1174993247..1e20b41c7b1 100644 --- a/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java @@ -61,7 +61,7 @@ private StackPane getRelatedArticlesPane(BibEntry entry) { progress.setMaxSize(100, 100); MrDLibFetcher fetcher = new MrDLibFetcher(Globals.prefs.get(JabRefPreferences.LANGUAGE), - Globals.BUILD_INFO.getVersion()); + Globals.BUILD_INFO.version); BackgroundTask .wrap(() -> fetcher.performSearch(entry)) .onRunning(() -> progress.setVisible(true)) diff --git a/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java b/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java index a47ba6f02be..dd3ac174be4 100644 --- a/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java +++ b/src/main/java/org/jabref/gui/errorconsole/ErrorConsoleViewModel.java @@ -92,7 +92,7 @@ public void clearLog() { public void reportIssue() { try { // System info - String systemInfo = String.format("JabRef %s%n%s %s %s %nJava %s", buildInfo.getVersion(), BuildInfo.OS, + String systemInfo = String.format("JabRef %s%n%s %s %s %nJava %s", buildInfo.version, BuildInfo.OS, BuildInfo.OS_VERSION, BuildInfo.OS_ARCH, BuildInfo.JAVA_VERSION); // Steps to reproduce String howToReproduce = "Steps to reproduce:\n\n1. ...\n2. ...\n3. ..."; diff --git a/src/main/java/org/jabref/gui/exporter/SaveAction.java b/src/main/java/org/jabref/gui/exporter/SaveAction.java new file mode 100644 index 00000000000..eadd350770a --- /dev/null +++ b/src/main/java/org/jabref/gui/exporter/SaveAction.java @@ -0,0 +1,51 @@ +package org.jabref.gui.exporter; + +import org.jabref.Globals; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; + +/** + * This class is just a simple wrapper for the soon to be refactored SaveDatabaseAction. + */ +public class SaveAction extends SimpleCommand { + + public enum SaveMethod { SAVE, SAVE_AS, SAVE_SELECTED } + + private final SaveMethod saveMethod; + private final JabRefFrame frame; + + public SaveAction(SaveMethod saveMethod, JabRefFrame frame, StateManager stateManager) { + this.saveMethod = saveMethod; + this.frame = frame; + + if (saveMethod == SaveMethod.SAVE_SELECTED) { + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); + } else { + this.executable.bind(ActionHelper.needsDatabase(stateManager)); + } + } + + @Override + public void execute() { + SaveDatabaseAction saveDatabaseAction = new SaveDatabaseAction( + frame.getCurrentBasePanel(), + Globals.prefs, + Globals.entryTypesManager); + + switch (saveMethod) { + case SAVE: + saveDatabaseAction.save(); + break; + case SAVE_AS: + saveDatabaseAction.saveAs(); + break; + case SAVE_SELECTED: + saveDatabaseAction.saveSelectedAsPlain(); + break; + default: + // Never happens + } + } +} diff --git a/src/main/java/org/jabref/gui/exporter/SaveAllAction.java b/src/main/java/org/jabref/gui/exporter/SaveAllAction.java index 4ac5618f991..0e9770c034f 100644 --- a/src/main/java/org/jabref/gui/exporter/SaveAllAction.java +++ b/src/main/java/org/jabref/gui/exporter/SaveAllAction.java @@ -1,9 +1,9 @@ package org.jabref.gui.exporter; +import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; -import org.jabref.gui.actions.Actions; import org.jabref.gui.actions.SimpleCommand; import org.jabref.logic.l10n.Localization; @@ -22,12 +22,10 @@ public void execute() { dialogService.notify(Localization.lang("Saving all libraries...")); for (BasePanel panel : frame.getBasePanelList()) { - if (!panel.getBibDatabaseContext().getDatabasePath().isPresent()) { - //It will ask a path before saving. - panel.runCommand(Actions.SAVE_AS); - } else { - panel.runCommand(Actions.SAVE); - // TODO: can we find out whether the save was actually done or not? + SaveDatabaseAction saveDatabaseAction = new SaveDatabaseAction(panel, Globals.prefs, Globals.entryTypesManager); + boolean saveResult = saveDatabaseAction.save(); + if (!saveResult) { + dialogService.notify(Localization.lang("Could not save file.")); } } diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java index 0a8f5b3ce28..aedef2b2534 100644 --- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java +++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java @@ -17,7 +17,7 @@ import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; -import org.jabref.gui.dialogs.AutosaveUIManager; +import org.jabref.gui.dialogs.AutosaveUiManager; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.logic.autosaveandbackup.AutosaveManager; @@ -47,89 +47,147 @@ * operation was canceled, or whether it was successful. */ public class SaveDatabaseAction { - - public enum SaveDatabaseMode { - SILENT, NORMAL - } - private static final Logger LOGGER = LoggerFactory.getLogger(SaveDatabaseAction.class); private final BasePanel panel; private final JabRefFrame frame; private final DialogService dialogService; - private final JabRefPreferences prefs; + private final JabRefPreferences preferences; private final BibEntryTypesManager entryTypesManager; - public SaveDatabaseAction(BasePanel panel, JabRefPreferences prefs, BibEntryTypesManager entryTypesManager) { + public enum SaveDatabaseMode { + SILENT, NORMAL + } + + public SaveDatabaseAction(BasePanel panel, JabRefPreferences preferences, BibEntryTypesManager entryTypesManager) { this.panel = panel; this.frame = panel.frame(); this.dialogService = frame.getDialogService(); - this.prefs = prefs; + this.preferences = preferences; this.entryTypesManager = entryTypesManager; } - private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, SavePreferences.DatabaseSaveType saveType) throws SaveException { - SavePreferences preferences = prefs.loadForSaveFromPreferences() - .withEncoding(encoding) - .withSaveType(saveType); - try (AtomicFileWriter fileWriter = new AtomicFileWriter(file, preferences.getEncoding(), preferences.makeBackup())) { - BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(fileWriter, preferences, entryTypesManager); + public boolean save() { + return save(panel.getBibDatabaseContext(), SaveDatabaseMode.NORMAL); + } - if (selectedOnly) { - databaseWriter.savePartOfDatabase(panel.getBibDatabaseContext(), panel.getSelectedEntries()); - } else { - databaseWriter.saveDatabase(panel.getBibDatabaseContext()); + public boolean save(SaveDatabaseMode mode) { + return save(panel.getBibDatabaseContext(), mode); + } + + /** + * Asks the user for the path and saves afterwards + */ + public void saveAs() { + askForSavePath().ifPresent(this::saveAs); + } + + public boolean saveAs(Path file) { + return this.saveAs(file, SaveDatabaseMode.NORMAL); + } + + public void saveSelectedAsPlain() { + askForSavePath().ifPresent(path -> { + try { + saveDatabase(path, true, preferences.getDefaultEncoding(), SavePreferences.DatabaseSaveType.PLAIN_BIBTEX); + frame.getFileHistory().newFile(path); + dialogService.notify(Localization.lang("Saved selected to '%0'.", path.toString())); + } catch (SaveException ex) { + LOGGER.error("A problem occurred when trying to save the file", ex); + dialogService.showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); } + }); + } - panel.registerUndoableChanges(databaseWriter.getSaveActionsFieldChanges()); + /** + * @param file the new file name to save the data base to. This is stored in the database context of the panel upon + * successful save. + * @return true on successful save + */ + boolean saveAs(Path file, SaveDatabaseMode mode) { + BibDatabaseContext context = panel.getBibDatabaseContext(); - if (fileWriter.hasEncodingProblems()) { - saveWithDifferentEncoding(file, selectedOnly, preferences.getEncoding(), fileWriter.getEncodingProblems(), saveType); + // Close AutosaveManager and BackupManager for original library + Optional databasePath = context.getDatabasePath(); + if (databasePath.isPresent()) { + final Path oldFile = databasePath.get(); + context.setDatabasePath(oldFile); + AutosaveManager.shutdown(context); + BackupManager.shutdown(context); + } + + // Set new location + if (context.getLocation() == DatabaseLocation.SHARED) { + // Save all properties dependent on the ID. This makes it possible to restore them. + new SharedDatabasePreferences(context.getDatabase().generateSharedDatabaseID()) + .putAllDBMSConnectionProperties(context.getDBMSSynchronizer().getConnectionProperties()); + } + + boolean saveResult = save(file, mode); + + if (saveResult) { + // we managed to successfully save the file + // thus, we can store the store the path into the context + context.setDatabasePath(file); + frame.refreshTitleAndTabs(); + + // Reinstall AutosaveManager and BackupManager for the new file name + panel.resetChangeMonitorAndChangePane(); + if (readyForAutosave(context)) { + AutosaveManager autosaver = AutosaveManager.start(context); + autosaver.registerListener(new AutosaveUiManager(panel)); } - } catch (UnsupportedCharsetException ex) { - throw new SaveException(Localization.lang("Character encoding '%0' is not supported.", encoding.displayName()), ex); - } catch (IOException ex) { - throw new SaveException("Problems saving:", ex); + if (readyForBackup(context)) { + BackupManager.start(context, entryTypesManager, preferences); + } + + frame.getFileHistory().newFile(file); } - return true; + return saveResult; } - private void saveWithDifferentEncoding(Path file, boolean selectedOnly, Charset encoding, Set encodingProblems, SavePreferences.DatabaseSaveType saveType) throws SaveException { - DialogPane pane = new DialogPane(); - VBox vbox = new VBox(); - vbox.getChildren().addAll( - new Text(Localization.lang("The chosen encoding '%0' could not encode the following characters:", encoding.displayName())), - new Text(encodingProblems.stream().map(Object::toString).collect(Collectors.joining("."))), - new Text(Localization.lang("What do you want to do?")) - ); - pane.setContent(vbox); - - ButtonType tryDifferentEncoding = new ButtonType(Localization.lang("Try different encoding"), ButtonBar.ButtonData.OTHER); - ButtonType ignore = new ButtonType(Localization.lang("Ignore"), ButtonBar.ButtonData.APPLY); - boolean saveWithDifferentEncoding = frame.getDialogService() - .showCustomDialogAndWait(Localization.lang("Save library"), pane, ignore, tryDifferentEncoding) - .filter(buttonType -> buttonType.equals(tryDifferentEncoding)) - .isPresent(); - if (saveWithDifferentEncoding) { - Optional newEncoding = frame.getDialogService().showChoiceDialogAndWait(Localization.lang("Save library"), Localization.lang("Select new encoding"), Localization.lang("Save library"), encoding, Encodings.getCharsets()); - if (newEncoding.isPresent()) { - // Make sure to remember which encoding we used. - panel.getBibDatabaseContext().getMetaData().setEncoding(newEncoding.get(), ChangePropagation.DO_NOT_POST_EVENT); + /** + * Asks the user for the path to save to. Stores the directory to the preferences, which is used next time when + * opening the dialog. + * + * @return the path set by the user + */ + private Optional askForSavePath() { + FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() + .addExtensionFilter(StandardFileType.BIBTEX_DB) + .withDefaultExtension(StandardFileType.BIBTEX_DB) + .withInitialDirectory(preferences.get(JabRefPreferences.WORKING_DIRECTORY)) + .build(); + Optional selectedPath = dialogService.showFileSaveDialog(fileDialogConfiguration); + selectedPath.ifPresent(path -> preferences.setWorkingDir(path.getParent())); + return selectedPath; + } - saveDatabase(file, selectedOnly, newEncoding.get(), saveType); + private boolean save(BibDatabaseContext bibDatabaseContext, SaveDatabaseMode mode) { + Optional databasePath = bibDatabaseContext.getDatabasePath(); + if (!databasePath.isPresent()) { + Optional savePath = askForSavePath(); + if (!savePath.isPresent()) { + return false; } + return saveAs(savePath.get(), mode); } + + return save(databasePath.get(), mode); } - private boolean doSave() { + private boolean save(Path targetPath, SaveDatabaseMode mode) { + if (mode == SaveDatabaseMode.NORMAL) { + dialogService.notify(String.format("%s...", Localization.lang("Saving library"))); + } + panel.setSaving(true); - Path targetPath = panel.getBibDatabaseContext().getDatabasePath().get(); try { Charset encoding = panel.getBibDatabaseContext() .getMetaData() .getEncoding() - .orElse(prefs.getDefaultEncoding()); + .orElse(preferences.getDefaultEncoding()); // Make sure to remember which encoding we used. panel.getBibDatabaseContext().getMetaData().setEncoding(encoding, ChangePropagation.DO_NOT_POST_EVENT); @@ -138,22 +196,18 @@ private boolean doSave() { if (success) { panel.getUndoManager().markUnchanged(); - // (Only) after a successful save the following - // statement marks that the base is unchanged - // since last save: + // After a successful save the following statement marks that the base is unchanged since last save panel.setNonUndoableChange(false); panel.setBaseChanged(false); - // Reset title of tab - frame.setTabTitle(panel, panel.getTabTitle(), - panel.getBibDatabaseContext().getDatabasePath().get().toAbsolutePath().toString()); + frame.setTabTitle(panel, panel.getTabTitle(), targetPath.toAbsolutePath().toString()); frame.setWindowTitle(); frame.updateAllTabTitles(); } return success; } catch (SaveException ex) { - LOGGER.error("A problem occurred when trying to save the file " + targetPath, ex); - frame.getDialogService().showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); + LOGGER.error(String.format("A problem occurred when trying to save the file %s", targetPath), ex); + dialogService.showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); return false; } finally { // release panel from save status @@ -161,82 +215,64 @@ private boolean doSave() { } } - public boolean save() { - return save(SaveDatabaseMode.NORMAL); - } + private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, SavePreferences.DatabaseSaveType saveType) throws SaveException { + SavePreferences preferences = this.preferences.loadForSaveFromPreferences() + .withEncoding(encoding) + .withSaveType(saveType); + try (AtomicFileWriter fileWriter = new AtomicFileWriter(file, preferences.getEncoding(), preferences.makeBackup())) { + BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter(fileWriter, preferences, entryTypesManager); - public boolean save(SaveDatabaseMode mode) { - if (panel.getBibDatabaseContext().getDatabasePath().isPresent()) { - if (mode == SaveDatabaseMode.NORMAL) { - panel.frame().getDialogService().notify(Localization.lang("Saving library") + "..."); - } - return doSave(); - } else { - Optional savePath = getSavePath(); - if (savePath.isPresent()) { - saveAs(savePath.get()); - return true; + if (selectedOnly) { + databaseWriter.savePartOfDatabase(panel.getBibDatabaseContext(), panel.getSelectedEntries()); + } else { + databaseWriter.saveDatabase(panel.getBibDatabaseContext()); } - } - return false; - } + panel.registerUndoableChanges(databaseWriter.getSaveActionsFieldChanges()); - public void saveAs() { - getSavePath().ifPresent(this::saveAs); - } + if (fileWriter.hasEncodingProblems()) { + saveWithDifferentEncoding(file, selectedOnly, preferences.getEncoding(), fileWriter.getEncodingProblems(), saveType); + } + } catch (UnsupportedCharsetException ex) { + throw new SaveException(Localization.lang("Character encoding '%0' is not supported.", encoding.displayName()), ex); + } catch (IOException ex) { + throw new SaveException("Problems saving:", ex); + } - private Optional getSavePath() { - FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() - .addExtensionFilter(StandardFileType.BIBTEX_DB) - .withDefaultExtension(StandardFileType.BIBTEX_DB) - .withInitialDirectory(prefs.get(JabRefPreferences.WORKING_DIRECTORY)) - .build(); - Optional selectedPath = dialogService.showFileSaveDialog(fileDialogConfiguration); - selectedPath.ifPresent(path -> prefs.setWorkingDir(path.getParent())); - return selectedPath; + return true; } - public void saveAs(Path file) { - BibDatabaseContext context = panel.getBibDatabaseContext(); - - // Close AutosaveManager and BackupManager for original library - Optional databasePath = context.getDatabasePath(); - if (databasePath.isPresent()) { - final Path oldFile = databasePath.get(); - context.setDatabaseFile(oldFile.toFile()); - AutosaveManager.shutdown(context); - BackupManager.shutdown(context); - } - - // Set new location - if (context.getLocation() == DatabaseLocation.SHARED) { - // Save all properties dependent on the ID. This makes it possible to restore them. - new SharedDatabasePreferences(context.getDatabase().generateSharedDatabaseID()) - .putAllDBMSConnectionProperties(context.getDBMSSynchronizer().getConnectionProperties()); - } - context.setDatabaseFile(file); + private void saveWithDifferentEncoding(Path file, boolean selectedOnly, Charset encoding, Set encodingProblems, SavePreferences.DatabaseSaveType saveType) throws SaveException { + DialogPane pane = new DialogPane(); + VBox vbox = new VBox(); + vbox.getChildren().addAll( + new Text(Localization.lang("The chosen encoding '%0' could not encode the following characters:", encoding.displayName())), + new Text(encodingProblems.stream().map(Object::toString).collect(Collectors.joining("."))), + new Text(Localization.lang("What do you want to do?")) + ); + pane.setContent(vbox); - // Save - save(); + ButtonType tryDifferentEncoding = new ButtonType(Localization.lang("Try different encoding"), ButtonBar.ButtonData.OTHER); + ButtonType ignore = new ButtonType(Localization.lang("Ignore"), ButtonBar.ButtonData.APPLY); + boolean saveWithDifferentEncoding = dialogService + .showCustomDialogAndWait(Localization.lang("Save library"), pane, ignore, tryDifferentEncoding) + .filter(buttonType -> buttonType.equals(tryDifferentEncoding)) + .isPresent(); + if (saveWithDifferentEncoding) { + Optional newEncoding = dialogService.showChoiceDialogAndWait(Localization.lang("Save library"), Localization.lang("Select new encoding"), Localization.lang("Save library"), encoding, Encodings.getCharsets()); + if (newEncoding.isPresent()) { + // Make sure to remember which encoding we used. + panel.getBibDatabaseContext().getMetaData().setEncoding(newEncoding.get(), ChangePropagation.DO_NOT_POST_EVENT); - // Reinstall AutosaveManager and BackupManager - panel.resetChangeMonitorAndChangePane(); - if (readyForAutosave(context)) { - AutosaveManager autosaver = AutosaveManager.start(context); - autosaver.registerListener(new AutosaveUIManager(panel)); - } - if (readyForBackup(context)) { - BackupManager.start(context, entryTypesManager, prefs); + saveDatabase(file, selectedOnly, newEncoding.get(), saveType); + } } - - context.getDatabasePath().ifPresent(presentFile -> frame.getFileHistory().newFile(presentFile)); } private boolean readyForAutosave(BibDatabaseContext context) { return ((context.getLocation() == DatabaseLocation.SHARED) || ((context.getLocation() == DatabaseLocation.LOCAL) - && prefs.getBoolean(JabRefPreferences.LOCAL_AUTO_SAVE))) + && preferences.getBoolean(JabRefPreferences.LOCAL_AUTO_SAVE))) && context.getDatabasePath().isPresent(); } @@ -244,17 +280,4 @@ private boolean readyForAutosave(BibDatabaseContext context) { private boolean readyForBackup(BibDatabaseContext context) { return (context.getLocation() == DatabaseLocation.LOCAL) && context.getDatabasePath().isPresent(); } - - public void saveSelectedAsPlain() { - getSavePath().ifPresent(path -> { - try { - saveDatabase(path, true, prefs.getDefaultEncoding(), SavePreferences.DatabaseSaveType.PLAIN_BIBTEX); - frame.getFileHistory().newFile(path); - frame.getDialogService().notify(Localization.lang("Saved selected to '%0'.", path.toString())); - } catch (SaveException ex) { - LOGGER.error("A problem occurred when trying to save the file", ex); - frame.getDialogService().showErrorDialogAndWait(Localization.lang("Save library"), Localization.lang("Could not save file."), ex); - } - }); - } } diff --git a/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java b/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java index 6b4c736c9e0..46c7166c67c 100644 --- a/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java +++ b/src/main/java/org/jabref/gui/exporter/WriteXMPAction.java @@ -21,9 +21,9 @@ import javafx.stage.Stage; import org.jabref.Globals; -import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.FXDialog; +import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.l10n.Localization; @@ -31,25 +31,28 @@ import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; +import static org.jabref.gui.actions.ActionHelper.needsDatabase; + public class WriteXMPAction extends SimpleCommand { - private final BasePanel basePanel; - private OptionsDialog optionsDialog; + private final StateManager stateManager; + private final DialogService dialogService; - private Collection entries; + private OptionsDialog optionsDialog; private BibDatabase database; + private Collection entries; private boolean shouldContinue = true; - private int skipped; private int entriesChanged; private int errors; - private final DialogService dialogService; - public WriteXMPAction(BasePanel basePanel) { - this.basePanel = basePanel; - dialogService = basePanel.frame().getDialogService(); + public WriteXMPAction(StateManager stateManager, DialogService dialogService) { + this.stateManager = stateManager; + this.dialogService = dialogService; + + this.executable.bind(needsDatabase(stateManager)); } @Override @@ -60,9 +63,13 @@ public void execute() { } public void init() { - database = basePanel.getDatabase(); + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } + + database = stateManager.getActiveDatabase().get().getDatabase(); // Get entries and check if it makes sense to perform this operation - entries = basePanel.getSelectedEntries(); + entries = stateManager.getSelectedEntries(); if (entries.isEmpty()) { @@ -97,7 +104,7 @@ public void init() { } private void writeXMP() { - if (!shouldContinue) { + if (!shouldContinue || stateManager.getActiveDatabase().isEmpty()) { return; } @@ -105,7 +112,7 @@ private void writeXMP() { // Make a list of all PDFs linked from this entry: List files = entry.getFiles().stream() .filter(file -> file.getFileType().equalsIgnoreCase("pdf")) - .map(file -> file.findIn(basePanel.getBibDatabaseContext(), Globals.prefs.getFilePreferences())) + .map(file -> file.findIn(stateManager.getActiveDatabase().get(), Globals.prefs.getFilePreferences())) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java index ea8b661957c..52002cba4dd 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java @@ -3,6 +3,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -10,8 +11,9 @@ import javafx.concurrent.Task; import org.jabref.Globals; -import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.fieldeditors.LinkedFileViewModel; @@ -20,9 +22,10 @@ import org.jabref.logic.importer.FulltextFetchers; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.URLDownload; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,28 +39,38 @@ public class DownloadFullTextAction extends SimpleCommand { // The minimum number of selected entries to ask the user for confirmation private static final int WARNING_LIMIT = 5; - private final BasePanel basePanel; private final DialogService dialogService; + private final StateManager stateManager; + private final PreferencesService preferences; - public DownloadFullTextAction(BasePanel basePanel) { - this.basePanel = basePanel; - this.dialogService = basePanel.frame().getDialogService(); + public DownloadFullTextAction(DialogService dialogService, StateManager stateManager, PreferencesService preferences) { + this.dialogService = dialogService; + this.stateManager = stateManager; + this.preferences = preferences; + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @Override public void execute() { - if (!basePanel.getSelectedEntries().isEmpty()) { - basePanel.output(Localization.lang("Looking for full text document...")); - } else { + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } + + List entries = stateManager.getSelectedEntries(); + if (entries.isEmpty()) { LOGGER.debug("No entry selected for fulltext download."); + return; } - if (basePanel.getSelectedEntries().size() >= WARNING_LIMIT) { + dialogService.notify(Localization.lang("Looking for full text document...")); + + if (entries.size() >= WARNING_LIMIT) { boolean confirmDownload = dialogService.showConfirmationDialogAndWait( Localization.lang("Download full text documents"), Localization.lang( "You are about to download full text documents for %0 entries.", - String.valueOf(basePanel.getSelectedEntries().size())) + "\n" + String.valueOf(stateManager.getSelectedEntries().size())) + "\n" + Localization.lang("JabRef will send at least one request per entry to a publisher.") + "\n" + Localization.lang("Do you still want to continue?"), @@ -65,26 +78,27 @@ public void execute() { Localization.lang("Cancel")); if (!confirmDownload) { - basePanel.output(Localization.lang("Operation canceled.")); + dialogService.notify(Localization.lang("Operation canceled.")); return; } } - Task>> findFullTextsTask = new Task>>() { + Task>> findFullTextsTask = new Task<>() { @Override protected Map> call() { Map> downloads = new ConcurrentHashMap<>(); int count = 0; - for (BibEntry entry : basePanel.getSelectedEntries()) { - FulltextFetchers fetchers = new FulltextFetchers(Globals.prefs.getImportFormatPreferences()); + for (BibEntry entry : entries) { + FulltextFetchers fetchers = new FulltextFetchers(preferences.getImportFormatPreferences()); downloads.put(entry, fetchers.findFullTextPDF(entry)); - updateProgress(++count, basePanel.getSelectedEntries().size()); + updateProgress(++count, entries.size()); } return downloads; } }; - findFullTextsTask.setOnSucceeded(value -> downloadFullTexts(findFullTextsTask.getValue())); + findFullTextsTask.setOnSucceeded(value -> + downloadFullTexts(findFullTextsTask.getValue(), stateManager.getActiveDatabase().get())); dialogService.showProgressDialogAndWait( Localization.lang("Download full text documents"), @@ -94,21 +108,22 @@ protected Map> call() { Globals.TASK_EXECUTOR.execute(findFullTextsTask); } - private void downloadFullTexts(Map> downloads) { + private void downloadFullTexts(Map> downloads, BibDatabaseContext databaseContext) { for (Map.Entry> download : downloads.entrySet()) { BibEntry entry = download.getKey(); Optional result = download.getValue(); if (result.isPresent()) { - Optional dir = basePanel.getBibDatabaseContext().getFirstExistingFileDir(Globals.prefs.getFilePreferences()); + Optional dir = databaseContext.getFirstExistingFileDir(Globals.prefs.getFilePreferences()); if (dir.isEmpty()) { dialogService.showErrorDialogAndWait(Localization.lang("Directory not found"), - Localization.lang("Main file directory not set!") + " " + Localization.lang("Preferences") + Localization.lang("Main file directory not set!") + " " + + Localization.lang("Preferences") + " -> " + Localization.lang("File")); return; } // Download and link full text - addLinkedFileFromURL(result.get(), entry, dir.get()); + addLinkedFileFromURL(databaseContext, result.get(), entry, dir.get()); } else { dialogService.notify(Localization.lang("No full text document found for entry %0.", entry.getCiteKeyOptional().orElse(Localization.lang("undefined")))); @@ -120,22 +135,23 @@ private void downloadFullTexts(Map> downloads) { * This method attaches a linked file from a URL (if not already linked) to an entry using the key and value pair * from the findFullTexts map and then downloads the file into the given targetDirectory * - * @param url the url "key" - * @param entry the entry "value" + * @param databaseContext the active database + * @param url the url "key" + * @param entry the entry "value" * @param targetDirectory the target directory for the downloaded file */ - private void addLinkedFileFromURL(URL url, BibEntry entry, Path targetDirectory) { + private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, BibEntry entry, Path targetDirectory) { LinkedFile newLinkedFile = new LinkedFile(url, ""); if (!entry.getFiles().contains(newLinkedFile)) { LinkedFileViewModel onlineFile = new LinkedFileViewModel( newLinkedFile, entry, - basePanel.getBibDatabaseContext(), + databaseContext, Globals.TASK_EXECUTOR, dialogService, - JabRefPreferences.getInstance().getXMPPreferences(), - JabRefPreferences.getInstance().getFilePreferences(), + preferences.getXMPPreferences(), + preferences.getFilePreferences(), ExternalFileTypes.getInstance()); try { @@ -144,7 +160,8 @@ private void addLinkedFileFromURL(URL url, BibEntry entry, Path targetDirectory) downloadTask.onSuccess(destination -> { LinkedFile downloadedFile = LinkedFilesEditorViewModel.fromFile( destination, - basePanel.getBibDatabaseContext().getFileDirectoriesAsPaths(JabRefPreferences.getInstance().getFilePreferences()), ExternalFileTypes.getInstance()); + databaseContext.getFileDirectoriesAsPaths(preferences.getFilePreferences()), + ExternalFileTypes.getInstance()); entry.addFile(downloadedFile); dialogService.notify(Localization.lang("Finished downloading full text document for entry %0.", entry.getCiteKeyOptional().orElse(Localization.lang("undefined")))); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index 949a05b559f..d52a24b9f95 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -223,30 +223,22 @@ public void renameFileToName(String targetFileName) { } private void performRenameWithConflictCheck(String targetFileName) { - Optional fileConflictCheck = linkedFileHandler.findExistingFile(linkedFile, entry, targetFileName); - if (fileConflictCheck.isPresent()) { - boolean confirmOverwrite = dialogService.showConfirmationDialogAndWait( + Optional existingFile = linkedFileHandler.findExistingFile(linkedFile, entry, targetFileName); + boolean overwriteFile = false; + + if (existingFile.isPresent()) { + overwriteFile = dialogService.showConfirmationDialogAndWait( Localization.lang("File exists"), Localization.lang("'%0' exists. Overwrite file?", targetFileName), Localization.lang("Overwrite")); - if (confirmOverwrite) { - try { - Files.delete(fileConflictCheck.get()); - } catch (IOException e) { - dialogService.showErrorDialogAndWait( - Localization.lang("Rename failed"), - Localization.lang("JabRef cannot access the file because it is being used by another process."), - e); - return; - } - } else { + if (!overwriteFile) { return; } } try { - linkedFileHandler.renameToName(targetFileName); + linkedFileHandler.renameToName(targetFileName, overwriteFile); } catch (IOException e) { dialogService.showErrorDialogAndWait(Localization.lang("Rename failed"), Localization.lang("JabRef cannot access the file because it is being used by another process.")); } @@ -284,6 +276,7 @@ public void moveToDefaultDirectory() { /** * Gets the filename for the current linked file and compares it to the new suggested filename. + * * @return true if the suggested filename is same as current filename. */ public boolean isGeneratedNameSameAsOriginal() { @@ -296,6 +289,7 @@ public boolean isGeneratedNameSameAsOriginal() { /** * Compares suggested filepath of current linkedFile with existing filepath. + * * @return true if suggested filepath is same as existing filepath. */ public boolean isGeneratedPathSameAsOriginal() { @@ -421,10 +415,10 @@ public BackgroundTask prepareDownloadTask(Path targetDirectory, URLDownloa BackgroundTask downloadTask = BackgroundTask .wrap(() -> { Optional suggestedType = inferFileType(urlDownload); - String suggestedTypeName = suggestedType.orElse(StandardExternalFileType.PDF).getName(); + ExternalFileType externalFileType = suggestedType.orElse(StandardExternalFileType.PDF); + String suggestedTypeName = externalFileType.getName(); linkedFile.setFileType(suggestedTypeName); - - String suggestedName = linkedFileHandler.getSuggestedFileName(suggestedTypeName); + String suggestedName = linkedFileHandler.getSuggestedFileName(externalFileType.getExtension()); return targetDirectory.resolve(suggestedName); }) .then(destination -> new FileDownloadTask(urlDownload.getSource(), destination)) diff --git a/src/main/java/org/jabref/gui/filelist/AttachFileAction.java b/src/main/java/org/jabref/gui/filelist/AttachFileAction.java index 51fa192c115..e11bff8b4dc 100644 --- a/src/main/java/org/jabref/gui/filelist/AttachFileAction.java +++ b/src/main/java/org/jabref/gui/filelist/AttachFileAction.java @@ -1,12 +1,12 @@ package org.jabref.gui.filelist; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Optional; -import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.fieldeditors.LinkedFilesEditorViewModel; @@ -14,32 +14,45 @@ import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.logic.l10n.Localization; import org.jabref.model.FieldChange; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; public class AttachFileAction extends SimpleCommand { private final BasePanel panel; + private final StateManager stateManager; private final DialogService dialogService; + private final PreferencesService preferencesService; - public AttachFileAction(BasePanel panel, DialogService dialogService) { + public AttachFileAction(BasePanel panel, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { this.panel = panel; + this.stateManager = stateManager; this.dialogService = dialogService; + this.preferencesService = preferencesService; + + this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager)); } @Override public void execute() { - if (panel.getSelectedEntries().size() != 1) { + if (stateManager.getActiveDatabase().isEmpty()) { + dialogService.notify(Localization.lang("This operation requires an open library.")); + return; + } + + if (stateManager.getSelectedEntries().size() != 1) { dialogService.notify(Localization.lang("This operation requires exactly one item to be selected.")); return; } - BibEntry entry = panel.getSelectedEntries().get(0); + BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get(); + + BibEntry entry = stateManager.getSelectedEntries().get(0); - Path workingDirectory = panel.getBibDatabaseContext() - .getFirstExistingFileDir(Globals.prefs.getFilePreferences()) - .orElse(Paths.get(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY))); + Path workingDirectory = databaseContext.getFirstExistingFileDir(preferencesService.getFilePreferences()) + .orElse(preferencesService.getWorkingDir()); FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() .withInitialDirectory(workingDirectory) @@ -47,7 +60,7 @@ public void execute() { dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(newFile -> { LinkedFile linkedFile = LinkedFilesEditorViewModel.fromFile(newFile, - panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()), + databaseContext.getFileDirectoriesAsPaths(preferencesService.getFilePreferences()), ExternalFileTypes.getInstance()); LinkedFileEditDialogView dialog = new LinkedFileEditDialogView(linkedFile); diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index 2b0d70b4885..a20278ae64f 100644 --- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -39,6 +39,7 @@ import org.jabref.model.strings.StringUtil; import com.google.common.base.Enums; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import org.fxmisc.easybind.EasyBind; public class GroupNodeViewModel { @@ -204,7 +205,7 @@ private JabRefIcon createDefaultIcon() { } private Optional parseIcon(String iconCode) { - return Enums.getIfPresent(IconTheme.JabRefIcons.class, iconCode.toUpperCase(Locale.ENGLISH)) + return Enums.getIfPresent(MaterialDesignIcon.class, iconCode.toUpperCase(Locale.ENGLISH)) .toJavaUtil() .map(icon -> new InternalMaterialDesignIcon(getColor(), icon)); } diff --git a/src/main/java/org/jabref/gui/groups/GroupTree.css b/src/main/java/org/jabref/gui/groups/GroupTree.css index fc2d6f3a51a..cfd75e2de0a 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTree.css +++ b/src/main/java/org/jabref/gui/groups/GroupTree.css @@ -29,7 +29,7 @@ -fx-fill: -jr-group-hits-fg; } -.disclosureNodeColumn { +.expansionNodeColumn { -fx-alignment: top-right; } @@ -73,7 +73,7 @@ -fx-padding: 0.40em 0.2em 0.40em 0em; } -.tree-table-row-cell:root > .disclosureNodeColumn { +.tree-table-row-cell:root > .expansionNodeColumn { -fx-padding: 0.45em 0.2em 0.45em 0.2em; } @@ -102,13 +102,17 @@ -fx-translate-x: -0.4em; } -#barBottom { +#newGroupButton { + -fx-padding: 0.1em 1.5em 0.1em 1.5em; +} + +#groupFilterBar { -fx-background-color: -jr-sidepane-header-background; -fx-border-color: -jr-separator; -fx-border-width: 1 0 0 0; -fx-padding: 0em 1em 0em 1em; } -#barBottom .glyph-icon { +#groupFilterBar .glyph-icon { -fx-font-size: 2em; } diff --git a/src/main/java/org/jabref/gui/groups/GroupTree.fxml b/src/main/java/org/jabref/gui/groups/GroupTree.fxml index 4d5302c133f..8990ffada01 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTree.fxml +++ b/src/main/java/org/jabref/gui/groups/GroupTree.fxml @@ -7,42 +7,36 @@ - + + + + + +
- +
- - - - - - - - - - -
diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeView.java b/src/main/java/org/jabref/gui/groups/GroupTreeView.java index 8932d44fc97..c26db16fc85 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTreeView.java +++ b/src/main/java/org/jabref/gui/groups/GroupTreeView.java @@ -34,7 +34,6 @@ import org.jabref.gui.DialogService; import org.jabref.gui.DragAndDropDataFormats; -import org.jabref.gui.GUIGlobals; import org.jabref.gui.StateManager; import org.jabref.gui.util.BindingsHelper; import org.jabref.gui.util.ControlHelper; @@ -62,7 +61,7 @@ public class GroupTreeView { @FXML private TreeTableView groupTree; @FXML private TreeTableColumn mainColumn; @FXML private TreeTableColumn numberColumn; - @FXML private TreeTableColumn disclosureNodeColumn; + @FXML private TreeTableColumn expansionNodeColumn; @FXML private CustomTextField searchField; @Inject private StateManager stateManager; @@ -77,7 +76,7 @@ public class GroupTreeView { @FXML public void initialize() { - this.localDragboard = GUIGlobals.localDragboard; + this.localDragboard = stateManager.getLocalDragboard(); viewModel = new GroupTreeViewModel(stateManager, dialogService, preferencesService, taskExecutor, localDragboard); // Set-up groups tree @@ -159,7 +158,7 @@ public void initialize() { group.toggleExpansion(); event.consume(); }) - .install(disclosureNodeColumn); + .install(expansionNodeColumn); // Set pseudo-classes to indicate if row is root or sub-item ( > 1 deep) PseudoClass rootPseudoClass = PseudoClass.getPseudoClass("root"); diff --git a/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java b/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java index f6534230b57..b6ab45a5016 100644 --- a/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java +++ b/src/main/java/org/jabref/gui/help/AboutDialogViewModel.java @@ -43,7 +43,7 @@ public class AboutDialogViewModel extends AbstractViewModel { public AboutDialogViewModel(DialogService dialogService, ClipBoardManager clipBoardManager, BuildInfo buildInfo) { this.dialogService = Objects.requireNonNull(dialogService); this.clipBoardManager = Objects.requireNonNull(clipBoardManager); - String[] version = buildInfo.getVersion().getFullVersion().split("--"); + String[] version = buildInfo.version.getFullVersion().split("--"); heading.set("JabRef " + version[0]); if (version.length == 1) { @@ -54,11 +54,11 @@ public AboutDialogViewModel(DialogService dialogService, ClipBoardManager clipBo Collectors.joining("--")); developmentVersion.set(dev); } - developers.set(buildInfo.getDevelopers()); - authors.set(buildInfo.getAuthors()); + developers.set(buildInfo.developers); + authors.set(buildInfo.authors); license.set(Localization.lang("License") + ":"); - changelogUrl = buildInfo.getVersion().getChangelogUrl(); - versionInfo = String.format("JabRef %s%n%s %s %s %nJava %s", buildInfo.getVersion(), BuildInfo.OS, + changelogUrl = buildInfo.version.getChangelogUrl(); + versionInfo = String.format("JabRef %s%n%s %s %s %nJava %s", buildInfo.version, BuildInfo.OS, BuildInfo.OS_VERSION, BuildInfo.OS_ARCH, BuildInfo.JAVA_VERSION); } diff --git a/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java b/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java index 3e85c2fc9e1..5e39a0e6d7c 100644 --- a/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java +++ b/src/main/java/org/jabref/gui/help/SearchForUpdateAction.java @@ -22,7 +22,7 @@ public SearchForUpdateAction(BuildInfo buildInfo, VersionPreferences versionPref @Override public void execute() { - new VersionWorker(buildInfo.getVersion(), versionPreferences.getIgnoredVersion(), dialogService, taskExecutor) + new VersionWorker(buildInfo.version, versionPreferences.getIgnoredVersion(), dialogService, taskExecutor) .checkForNewVersionAsync(); } } diff --git a/src/main/java/org/jabref/gui/icon/IconTheme.java b/src/main/java/org/jabref/gui/icon/IconTheme.java index 0faa4d0eb7a..3e7efb6cd8a 100644 --- a/src/main/java/org/jabref/gui/icon/IconTheme.java +++ b/src/main/java/org/jabref/gui/icon/IconTheme.java @@ -17,8 +17,7 @@ import javafx.scene.control.ToggleButton; import javafx.scene.image.Image; import javafx.scene.paint.Color; - -import org.jabref.preferences.JabRefPreferences; +import javafx.scene.text.Font; import de.jensd.fx.glyphs.GlyphIcons; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; @@ -27,22 +26,21 @@ public class IconTheme { - public static final Color DEFAULT_DISABLED_COLOR = JabRefPreferences.getInstance().getColor(JabRefPreferences.ICON_DISABLED_COLOR); - public static final javafx.scene.paint.Color SELECTED_COLOR = javafx.scene.paint.Color.web("#50618F"); + public static final Color DEFAULT_DISABLED_COLOR = Color.web("#c8c8c8"); + public static final Color SELECTED_COLOR = Color.web("#50618F"); private static final String DEFAULT_ICON_PATH = "/images/external/red.png"; private static final Logger LOGGER = LoggerFactory.getLogger(IconTheme.class); - private static final Map KEY_TO_ICON = readIconThemeFile( - IconTheme.class.getResource("/images/Icons.properties"), "/images/external/"); + private static final Map KEY_TO_ICON = readIconThemeFile(IconTheme.class.getResource("/images/Icons.properties"), "/images/external/"); public static void loadFonts() { try (InputStream stream = getMaterialDesignIconsStream()) { - javafx.scene.text.Font.loadFont(stream, 7); + Font.loadFont(stream, 7); } catch (IOException e) { LOGGER.error("Error loading Material Design Icons TTF font", e); } try (InputStream stream = getJabRefMaterialDesignIconsStream()) { - javafx.scene.text.Font.loadFont(stream, 7); + Font.loadFont(stream, 7); } catch (IOException e) { LOGGER.error("Error loading custom font for custom JabRef icons", e); } @@ -86,7 +84,7 @@ public static URL getIconUrl(String name) { String key = Objects.requireNonNull(name, "icon name"); if (!KEY_TO_ICON.containsKey(key)) { LOGGER.warn("Could not find icon url by name " + name + ", so falling back on default icon " - + DEFAULT_ICON_PATH); + + DEFAULT_ICON_PATH); } String path = KEY_TO_ICON.getOrDefault(key, DEFAULT_ICON_PATH); return Objects.requireNonNull(IconTheme.class.getResource(path), "Path must not be null for key " + key); @@ -110,7 +108,7 @@ private static Map readIconThemeFile(URL url, String prefix) { Map result = new HashMap<>(); try (BufferedReader in = new BufferedReader( - new InputStreamReader(url.openStream(), StandardCharsets.ISO_8859_1))) { + new InputStreamReader(url.openStream(), StandardCharsets.ISO_8859_1))) { String line; while ((line = in.readLine()) != null) { if (!line.contains("=")) { @@ -230,6 +228,7 @@ public enum JabRefIcons implements JabRefIcon { GITHUB(MaterialDesignIcon.GITHUB_CIRCLE), /*css: github-circle*/ TOGGLE_ENTRY_PREVIEW(MaterialDesignIcon.LIBRARY_BOOKS), /*css: library-books */ TOGGLE_GROUPS(MaterialDesignIcon.VIEW_LIST), /*css: view-list */ + SHOW_PREFERENCES_LIST(MaterialDesignIcon.VIEW_LIST), /*css: view-list */ WRITE_XMP(MaterialDesignIcon.IMPORT), /* css: import */ FILE_WORD(MaterialDesignIcon.FILE_WORD), /*css: file-word */ FILE_EXCEL(MaterialDesignIcon.FILE_EXCEL), /*css: file-excel */ @@ -345,7 +344,6 @@ public ToggleButton asToggleButton() { @Override public JabRefIcon withColor(Color color) { return icon.withColor(color); - } @Override @@ -353,5 +351,4 @@ public JabRefIcon disabled() { return icon.disabled(); } } - } diff --git a/src/main/java/org/jabref/gui/importer/actions/AppendDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/AppendDatabaseAction.java index e2f60a56aa5..60feebd3548 100644 --- a/src/main/java/org/jabref/gui/importer/actions/AppendDatabaseAction.java +++ b/src/main/java/org/jabref/gui/importer/actions/AppendDatabaseAction.java @@ -12,7 +12,9 @@ import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; -import org.jabref.gui.actions.BaseAction; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.importer.AppendDatabaseDialog; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableInsertEntries; @@ -41,18 +43,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AppendDatabaseAction implements BaseAction { +public class AppendDatabaseAction extends SimpleCommand { private static final Logger LOGGER = LoggerFactory.getLogger(AppendDatabaseAction.class); - private final BasePanel panel; + private final JabRefFrame frame; + private final DialogService dialogService; + private final StateManager stateManager; private final List filesToOpen = new ArrayList<>(); - private final DialogService dialogService; - public AppendDatabaseAction(JabRefFrame frame, BasePanel panel) { - this.panel = panel; - dialogService = frame.getDialogService(); + public AppendDatabaseAction(JabRefFrame frame, DialogService dialogService, StateManager stateManager) { + this.frame = frame; + this.dialogService = dialogService; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsDatabase(stateManager)); } private static void mergeFromBibtex(BasePanel panel, ParserResult parserResult, boolean importEntries, @@ -131,9 +137,9 @@ private static void addGroups(GroupTreeNode newGroups, CompoundEdit ce) { } Globals.stateManager.getActiveDatabase() - .map(BibDatabaseContext::getMetaData) - .flatMap(MetaData::getGroups) - .ifPresent(newGroups::moveTo); + .map(BibDatabaseContext::getMetaData) + .flatMap(MetaData::getGroups) + .ifPresent(newGroups::moveTo); //UndoableAddOrRemoveGroup undo = new UndoableAddOrRemoveGroup(groupsRoot, // new GroupTreeNodeViewModel(newGroups), UndoableAddOrRemoveGroup.ADD_NODE); @@ -141,7 +147,13 @@ private static void addGroups(GroupTreeNode newGroups, CompoundEdit ce) { } @Override - public void action() { + public void execute() { + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } + + BasePanel panel = frame.getCurrentBasePanel(); + filesToOpen.clear(); final AppendDatabaseDialog dialog = new AppendDatabaseDialog(); Optional response = dialog.showAndWait(); @@ -163,25 +175,25 @@ public void action() { for (Path file : filesToOpen) { // Run the actual open in a thread to prevent the program locking until the file is loaded. - BackgroundTask.wrap(() -> openIt(file, dialog.importEntries(), dialog.importStrings(), dialog.importGroups(), dialog.importSelectorWords())) + BackgroundTask.wrap(() -> openIt(panel, file, dialog.importEntries(), dialog.importStrings(), dialog.importGroups(), dialog.importSelectorWords())) .onSuccess(fileName -> dialogService.notify(Localization.lang("Imported from library") + " '" + fileName + "'")) .onFailure(exception -> { LOGGER.warn("Could not open database", exception); - dialogService.showErrorDialogAndWait(Localization.lang("Open library"), exception);}) + dialogService.showErrorDialogAndWait(Localization.lang("Open library"), exception); + }) .executeWith(Globals.TASK_EXECUTOR); } } } - private String openIt(Path file, boolean importEntries, boolean importStrings, boolean importGroups, - boolean importSelectorWords) throws IOException, KeyCollisionException { - Globals.prefs.put(JabRefPreferences.WORKING_DIRECTORY, file.getParent().toString()); - // Should this be done _after_ we know it was successfully opened? + private String openIt(BasePanel panel, Path file, boolean importEntries, boolean importStrings, boolean importGroups, + boolean importSelectorWords) throws IOException, KeyCollisionException { + Globals.prefs.put(JabRefPreferences.WORKING_DIRECTORY, file.getParent().toString()); + // Should this be done _after_ we know it was successfully opened? ParserResult parserResult = OpenDatabase.loadDatabase(file, - Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor()); - AppendDatabaseAction.mergeFromBibtex(panel, parserResult, importEntries, importStrings, importGroups, - importSelectorWords); - return file.toString(); + Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor()); + AppendDatabaseAction.mergeFromBibtex(panel, parserResult, importEntries, importStrings, importGroups, + importSelectorWords); + return file.toString(); } - } diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java index 50717a536e3..a9701ce1b90 100644 --- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java +++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java @@ -3,6 +3,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -42,11 +43,11 @@ public class OpenDatabaseAction extends SimpleCommand { // List of actions that may need to be called after opening the file. Such as // upgrade actions etc. that may depend on the JabRef version that wrote the file: private static final List POST_OPEN_ACTIONS = Arrays.asList( - // Migrations: - // Warning for migrating the Review into the Comment field - new MergeReviewIntoCommentAction(), - // Check for new custom entry types loaded from the BIB file: - new CheckForNewEntryTypesAction()); + // Migrations: + // Warning for migrating the Review into the Comment field + new MergeReviewIntoCommentAction(), + // Check for new custom entry types loaded from the BIB file: + new CheckForNewEntryTypesAction()); private final JabRefFrame frame; private final DialogService dialogService; @@ -84,7 +85,6 @@ public void execute() { } /** - * * @return Path of current panel database directory or the working directory */ private Path getInitialDirectory() { @@ -102,7 +102,7 @@ private Path getInitialDirectory() { * @param file the file, may be null or not existing */ public void openFile(Path file, boolean raisePanel) { - openFiles(Arrays.asList(file), raisePanel); + openFiles(new ArrayList<>(List.of(file)), raisePanel); } /** @@ -116,12 +116,12 @@ public void openFiles(List filesToOpen, boolean raisePanel) { int removed = 0; // Check if any of the files are already open: - for (Iterator iterator = filesToOpen.iterator(); iterator.hasNext();) { + for (Iterator iterator = filesToOpen.iterator(); iterator.hasNext(); ) { Path file = iterator.next(); for (int i = 0; i < frame.getTabbedPane().getTabs().size(); i++) { BasePanel basePanel = frame.getBasePanelAt(i); if ((basePanel.getBibDatabaseContext().getDatabasePath().isPresent()) - && basePanel.getBibDatabaseContext().getDatabasePath().get().equals(file)) { + && basePanel.getBibDatabaseContext().getDatabasePath().get().equals(file)) { iterator.remove(); removed++; // See if we removed the final one. If so, we must perhaps @@ -169,10 +169,9 @@ private void openTheFile(Path file, boolean raisePanel) { OpenDatabaseAction.performPostOpenActions(panel, result); }) .onFailure(ex -> dialogService.showErrorDialogAndWait(Localization.lang("Connection error"), - ex.getMessage() + "\n\n" + Localization.lang("A local copy will be opened."))) + ex.getMessage() + "\n\n" + Localization.lang("A local copy will be opened."))) .executeWith(Globals.TASK_EXECUTOR); } - } private ParserResult loadDatabase(Path file) throws Exception { @@ -187,23 +186,21 @@ private ParserResult loadDatabase(Path file) throws Exception { } ParserResult result = OpenDatabase.loadDatabase(fileToLoad.toString(), - Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor()); + Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor()); if (result.getDatabase().isShared()) { try { new SharedDatabaseUIManager(frame).openSharedDatabaseFromParserResult(result); } catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException | - NotASharedDatabaseException e) { + NotASharedDatabaseException e) { result.getDatabaseContext().clearDatabaseFile(); // do not open the original file result.getDatabase().clearSharedDatabaseID(); LOGGER.error("Connection error", e); throw e; - } } return result; - } private BasePanel addNewDatabase(ParserResult result, final Path file, boolean raisePanel) { @@ -214,6 +211,5 @@ private BasePanel addNewDatabase(ParserResult result, final Path file, boolean r BasePanel basePanel = new BasePanel(frame, BasePanelPreferences.from(Globals.prefs), result.getDatabaseContext(), ExternalFileTypes.getInstance()); frame.addTab(basePanel, raisePanel); return basePanel; - } } diff --git a/src/main/java/org/jabref/gui/journals/AbbreviateAction.java b/src/main/java/org/jabref/gui/journals/AbbreviateAction.java index 6fe009928cd..b28ea1e9680 100644 --- a/src/main/java/org/jabref/gui/journals/AbbreviateAction.java +++ b/src/main/java/org/jabref/gui/journals/AbbreviateAction.java @@ -9,13 +9,19 @@ import org.jabref.Globals; import org.jabref.JabRefExecutorService; -import org.jabref.gui.BasePanel; -import org.jabref.gui.actions.BaseAction; +import org.jabref.gui.DialogService; +import org.jabref.gui.JabRefFrame; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.actions.StandardActions; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.FieldFactory; +import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,38 +29,83 @@ /** * Converts journal full names to either iso or medline abbreviations for all selected entries. */ -public class AbbreviateAction implements BaseAction { +public class AbbreviateAction extends SimpleCommand { private static final Logger LOGGER = LoggerFactory.getLogger(AbbreviateAction.class); - private final BasePanel panel; - private final AbbreviationType abbreviationType; - public AbbreviateAction(BasePanel panel, AbbreviationType abbreviationType) { - this.panel = panel; - this.abbreviationType = abbreviationType; + private final StandardActions action; + private final JabRefFrame frame; + private final DialogService dialogService; + private final StateManager stateManager; + private final PreferencesService preferences; + + private AbbreviationType abbreviationType; + + public AbbreviateAction(StandardActions action, + JabRefFrame frame, + DialogService dialogService, + StateManager stateManager, + PreferencesService preferences) { + + this.action = action; + this.frame = frame; + this.dialogService = dialogService; + this.stateManager = stateManager; + this.preferences = preferences; + + switch (action) { + case ABBREVIATE_DEFAULT: + abbreviationType = AbbreviationType.DEFAULT; + break; + case ABBREVIATE_MEDLINE: + abbreviationType = AbbreviationType.MEDLINE; + break; + case ABBREVIATE_SHORTEST_UNIQUE: + abbreviationType = AbbreviationType.SHORTEST_UNIQUE; + break; + default: + LOGGER.debug("Unknown action: " + action.name()); + break; + } + + this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @Override - public void action() { - panel.output(Localization.lang("Abbreviating...")); - BackgroundTask.wrap(this::abbreviate) - .onSuccess(panel::output) - .executeWith(Globals.TASK_EXECUTOR); + public void execute() { + + if (action == StandardActions.ABBREVIATE_DEFAULT + || action == StandardActions.ABBREVIATE_MEDLINE + || action == StandardActions.ABBREVIATE_SHORTEST_UNIQUE) { + + dialogService.notify(Localization.lang("Abbreviating...")); + stateManager.getActiveDatabase().ifPresent(databaseContext -> + BackgroundTask.wrap(() -> abbreviate(stateManager.getActiveDatabase().get(), stateManager.getSelectedEntries())) + .onSuccess(dialogService::notify) + .executeWith(Globals.TASK_EXECUTOR)); + } else if (action == StandardActions.UNABBREVIATE) { + + dialogService.notify(Localization.lang("Unabbreviating...")); + stateManager.getActiveDatabase().ifPresent(databaseContext -> + BackgroundTask.wrap(() -> unabbreviate(stateManager.getActiveDatabase().get(), stateManager.getSelectedEntries())) + .onSuccess(dialogService::notify) + .executeWith(Globals.TASK_EXECUTOR)); + } else { + LOGGER.debug("Unknown action: " + action.name()); + } } - private String abbreviate() { - List entries = panel.getSelectedEntries(); + private String abbreviate(BibDatabaseContext databaseContext, List entries) { UndoableAbbreviator undoableAbbreviator = new UndoableAbbreviator( - Globals.journalAbbreviationLoader.getRepository(Globals.prefs.getJournalAbbreviationPreferences()), + Globals.journalAbbreviationLoader.getRepository(preferences.getJournalAbbreviationPreferences()), abbreviationType); NamedCompound ce = new NamedCompound(Localization.lang("Abbreviate journal names")); // Collect all callables to execute in one collection. - Set> tasks = entries.stream() - .>map(entry -> () -> - FieldFactory.getJournalNameFields().stream().anyMatch(journalField -> - undoableAbbreviator.abbreviate(panel.getDatabase(), entry, journalField, ce))) + Set> tasks = entries.stream().>map(entry -> () -> + FieldFactory.getJournalNameFields().stream().anyMatch(journalField -> + undoableAbbreviator.abbreviate(databaseContext.getDatabase(), entry, journalField, ce))) .collect(Collectors.toSet()); // Execute the callables and wait for the results. @@ -72,10 +123,27 @@ private String abbreviate() { if (count > 0) { ce.end(); - panel.getUndoManager().addEdit(ce); - panel.markBaseChanged(); + frame.getUndoManager().addEdit(ce); + frame.getCurrentBasePanel().markBaseChanged(); return Localization.lang("Abbreviated %0 journal names.", String.valueOf(count)); } return Localization.lang("No journal names could be abbreviated."); } + + private String unabbreviate(BibDatabaseContext databaseContext, List entries) { + UndoableUnabbreviator undoableAbbreviator = new UndoableUnabbreviator(Globals.journalAbbreviationLoader + .getRepository(Globals.prefs.getJournalAbbreviationPreferences())); + + NamedCompound ce = new NamedCompound(Localization.lang("Unabbreviate journal names")); + int count = entries.stream().mapToInt(entry -> + (int) FieldFactory.getJournalNameFields().stream().filter(journalField -> + undoableAbbreviator.unabbreviate(databaseContext.getDatabase(), entry, journalField, ce)).count()).sum(); + if (count > 0) { + ce.end(); + frame.getUndoManager().addEdit(ce); + frame.getCurrentBasePanel().markBaseChanged(); + return Localization.lang("Unabbreviated %0 journal names.", String.valueOf(count)); + } + return Localization.lang("No journal names could be unabbreviated."); + } } diff --git a/src/main/java/org/jabref/gui/journals/UnabbreviateAction.java b/src/main/java/org/jabref/gui/journals/UnabbreviateAction.java deleted file mode 100644 index a67fb8eb4bf..00000000000 --- a/src/main/java/org/jabref/gui/journals/UnabbreviateAction.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.jabref.gui.journals; - -import java.util.List; - -import org.jabref.Globals; -import org.jabref.gui.BasePanel; -import org.jabref.gui.actions.BaseAction; -import org.jabref.gui.undo.NamedCompound; -import org.jabref.gui.util.BackgroundTask; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.FieldFactory; - -/** - * Converts journal abbreviations back to full name for all selected entries. - */ -public class UnabbreviateAction implements BaseAction { - - private final BasePanel panel; - - public UnabbreviateAction(BasePanel panel) { - this.panel = panel; - } - - @Override - public void action() { - panel.output(Localization.lang("Unabbreviating...")); - BackgroundTask.wrap(this::unabbreviate) - .onSuccess(panel::output) - .executeWith(Globals.TASK_EXECUTOR); - } - - private String unabbreviate() { - List entries = panel.getSelectedEntries(); // Never null - - UndoableUnabbreviator undoableAbbreviator = new UndoableUnabbreviator(Globals.journalAbbreviationLoader - .getRepository(Globals.prefs.getJournalAbbreviationPreferences())); - - NamedCompound ce = new NamedCompound(Localization.lang("Unabbreviate journal names")); - int count = entries.stream().mapToInt(entry -> - (int) FieldFactory.getJournalNameFields().stream().filter(journalField -> - undoableAbbreviator.unabbreviate(panel.getDatabase(), entry, journalField, ce)).count()).sum(); - if (count > 0) { - ce.end(); - panel.getUndoManager().addEdit(ce); - panel.markBaseChanged(); - return Localization.lang("Unabbreviated %0 journal names.", String.valueOf(count)); - } - return Localization.lang("No journal names could be unabbreviated."); - } -} diff --git a/src/main/java/org/jabref/gui/keyboard/KeyBinding.java b/src/main/java/org/jabref/gui/keyboard/KeyBinding.java index 7c4f66a193d..d2f8db9e875 100644 --- a/src/main/java/org/jabref/gui/keyboard/KeyBinding.java +++ b/src/main/java/org/jabref/gui/keyboard/KeyBinding.java @@ -23,7 +23,6 @@ public enum KeyBinding { CUT("Cut", Localization.lang("Cut"), "ctrl+X", KeyBindingCategory.EDIT), //We have to put Entry Editor Previous before, because otherwise the decrease font size is found first ENTRY_EDITOR_PREVIOUS_PANEL_2("Entry editor, previous panel 2", Localization.lang("Entry editor, previous panel 2"), "ctrl+MINUS", KeyBindingCategory.VIEW), - DECREASE_TABLE_FONT_SIZE("Decrease table font size", Localization.lang("Decrease table font size"), "ctrl+MINUS", KeyBindingCategory.VIEW), DELETE_ENTRY("Delete entry", Localization.lang("Delete entry"), "DELETE", KeyBindingCategory.BIBTEX), DEFAULT_DIALOG_ACTION("Execute default action in dialog", Localization.lang("Execute default action in dialog"), "ctrl+ENTER", KeyBindingCategory.VIEW), DOWNLOAD_FULL_TEXT("Download full text documents", Localization.lang("Download full text documents"), "alt+F7", KeyBindingCategory.QUALITY), @@ -43,8 +42,6 @@ public enum KeyBinding { HELP("Help", Localization.lang("Help"), "F1", KeyBindingCategory.FILE), IMPORT_INTO_CURRENT_DATABASE("Import into current library", Localization.lang("Import into current library"), "ctrl+I", KeyBindingCategory.FILE), IMPORT_INTO_NEW_DATABASE("Import into new library", Localization.lang("Import into new library"), "ctrl+alt+I", KeyBindingCategory.FILE), - INCREASE_TABLE_FONT_SIZE("Increase table font size", Localization.lang("Increase table font size"), "ctrl+PLUS", KeyBindingCategory.VIEW), - DEFAULT_TABLE_FONT_SIZE("Default table font size", Localization.lang("Default table font size"), "ctrl+0", KeyBindingCategory.VIEW), NEW_ARTICLE("New article", Localization.lang("New article"), "ctrl+shift+A", KeyBindingCategory.BIBTEX), NEW_BOOK("New book", Localization.lang("New book"), "ctrl+shift+B", KeyBindingCategory.BIBTEX), NEW_ENTRY("New entry", Localization.lang("New entry"), "ctrl+N", KeyBindingCategory.BIBTEX), diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 935470b1599..24fa8753183 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -26,7 +26,6 @@ import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DragAndDropDataFormats; -import org.jabref.gui.GUIGlobals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.externalfiles.ImportHandler; import org.jabref.gui.externalfiletype.ExternalFileTypes; @@ -56,7 +55,7 @@ public class MainTable extends TableView { private final MainTableDataModel model; private final ImportHandler importHandler; - private final CustomLocalDragboard localDragboard = GUIGlobals.localDragboard; + private final CustomLocalDragboard localDragboard; public MainTable(MainTableDataModel model, JabRefFrame frame, BasePanel panel, BibDatabaseContext database, @@ -76,6 +75,7 @@ public MainTable(MainTableDataModel model, JabRefFrame frame, Globals.getFileUpdateMonitor(), undoManager, Globals.stateManager); + localDragboard = Globals.stateManager.getLocalDragboard(); this.getColumns().addAll(new MainTableColumnFactory(database, preferences.getColumnPreferences(), externalFileTypes, panel.getUndoManager(), frame.getDialogService()).createColumns()); @@ -85,7 +85,7 @@ public MainTable(MainTableDataModel model, JabRefFrame frame, panel.showAndEdit(entry.getEntry()); } }) - .withContextMenu(entry -> RightClickMenu.create(entry, keyBindingRepository, panel, frame.getDialogService())) + .withContextMenu(entry -> RightClickMenu.create(entry, keyBindingRepository, panel, frame.getDialogService(), Globals.stateManager, Globals.prefs)) .setOnDragDetected(this::handleOnDragDetected) .setOnDragDropped(this::handleOnDragDropped) .setOnDragOver(this::handleOnDragOver) @@ -325,12 +325,4 @@ private Optional findEntry(BibEntry entry) { .filter(viewModel -> viewModel.getEntry().equals(entry)) .findFirst(); } - - /** - * Repaints the table with the most recent font configuration - */ - public void updateFont() { - // TODO: Font & padding customization - // setFont(GUIGlobals.currentFont); - } } diff --git a/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java new file mode 100644 index 00000000000..6579eed3102 --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/OpenExternalFileAction.java @@ -0,0 +1,56 @@ +package org.jabref.gui.maintable; + +import java.util.List; + +import org.jabref.Globals; +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.preferences.PreferencesService; + +public class OpenExternalFileAction extends SimpleCommand { + + private final DialogService dialogService; + private final StateManager stateManager; + private final PreferencesService preferencesService; + + public OpenExternalFileAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { + this.dialogService = dialogService; + this.stateManager = stateManager; + this.preferencesService = preferencesService; + + this.executable.bind(ActionHelper.isFieldSetForSelectedEntry(StandardField.FILE, stateManager) + .and(ActionHelper.needsEntriesSelected(1, stateManager))); + } + + @Override + public void execute() { + stateManager.getActiveDatabase().ifPresent(databaseContext -> { + final List selectedEntries = stateManager.getSelectedEntries(); + + if (selectedEntries.size() != 1) { + dialogService.notify(Localization.lang("This operation requires exactly one item to be selected.")); + return; + } + + final BibEntry entry = selectedEntries.get(0); + + LinkedFileViewModel linkedFileViewModel = new LinkedFileViewModel( + entry.getFiles().get(0), + entry, + databaseContext, + Globals.TASK_EXECUTOR, + dialogService, + preferencesService.getXMPPreferences(), + preferencesService.getFilePreferences(), + ExternalFileTypes.getInstance()); + linkedFileViewModel.open(); + }); + } +} diff --git a/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java new file mode 100644 index 00000000000..a2c3c211785 --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/OpenFolderAction.java @@ -0,0 +1,43 @@ +package org.jabref.gui.maintable; + +import org.jabref.Globals; +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.model.entry.field.StandardField; +import org.jabref.preferences.PreferencesService; + +public class OpenFolderAction extends SimpleCommand { + + private final DialogService dialogService; + private final StateManager stateManager; + private final PreferencesService preferencesService; + + public OpenFolderAction(DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { + this.dialogService = dialogService; + this.stateManager = stateManager; + this.preferencesService = preferencesService; + + this.executable.bind(ActionHelper.isFieldSetForSelectedEntry(StandardField.FILE, stateManager)); + } + + @Override + public void execute() { + stateManager.getActiveDatabase().ifPresent(databaseContext -> + stateManager.getSelectedEntries().forEach(entry -> { + LinkedFileViewModel linkedFileViewModel = new LinkedFileViewModel( + entry.getFiles().get(0), + entry, + databaseContext, + Globals.TASK_EXECUTOR, + dialogService, + preferencesService.getXMPPreferences(), + preferencesService.getFilePreferences(), + ExternalFileTypes.getInstance()); + linkedFileViewModel.openFolder(); + })); + } +} diff --git a/src/main/java/org/jabref/gui/maintable/OpenUrlAction.java b/src/main/java/org/jabref/gui/maintable/OpenUrlAction.java new file mode 100644 index 00000000000..cfcd40b262c --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/OpenUrlAction.java @@ -0,0 +1,72 @@ +package org.jabref.gui.maintable; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import javafx.beans.binding.BooleanExpression; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.desktop.JabRefDesktop; +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.StandardField; + +public class OpenUrlAction extends SimpleCommand { + + private final DialogService dialogService; + private final StateManager stateManager; + + public OpenUrlAction(DialogService dialogService, StateManager stateManager) { + this.dialogService = dialogService; + this.stateManager = stateManager; + + BooleanExpression fieldIsSet = ActionHelper.isAnyFieldSetForSelectedEntry( + List.of(StandardField.URL, StandardField.DOI, StandardField.URI, StandardField.EPRINT), + stateManager); + this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager).and(fieldIsSet)); + } + + @Override + public void execute() { + stateManager.getActiveDatabase().ifPresent(databaseContext -> { + final List entries = stateManager.getSelectedEntries(); + + if (entries.size() != 1) { + dialogService.notify(Localization.lang("This operation requires exactly one item to be selected.")); + return; + } + + BibEntry entry = entries.get(0); + + // ToDo: Create dialog or menu to chose which one to open + // URL - DOI - DOI - EPRINT + Optional link = entry.getField(StandardField.EPRINT); + Field field = StandardField.EPRINT; + if (entry.hasField(StandardField.URI)) { + link = entry.getField(StandardField.URI); + field = StandardField.URI; + } + if (entry.hasField(StandardField.DOI)) { + link = entry.getField(StandardField.DOI); + field = StandardField.DOI; + } + if (entry.hasField(StandardField.URL)) { + link = entry.getField(StandardField.URL); + field = StandardField.URL; + } + + if (link.isPresent()) { + try { + JabRefDesktop.openExternalViewer(databaseContext, link.get(), field); + } catch (IOException e) { + dialogService.showErrorDialogAndWait(Localization.lang("Unable to open link."), e); + } + } + }); + } +} diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index 7029810a2fa..359569fbfd4 100644 --- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -1,8 +1,5 @@ package org.jabref.gui.maintable; -import java.util.Collections; -import java.util.List; - import javafx.scene.control.ContextMenu; import javafx.scene.control.Menu; import javafx.scene.control.SeparatorMenuItem; @@ -10,134 +7,99 @@ import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; +import org.jabref.gui.SendAsEMailAction; +import org.jabref.gui.StateManager; import org.jabref.gui.actions.ActionFactory; -import org.jabref.gui.actions.Actions; -import org.jabref.gui.actions.OldCommandWrapper; import org.jabref.gui.actions.StandardActions; +import org.jabref.gui.edit.CopyMoreAction; +import org.jabref.gui.edit.EditAction; import org.jabref.gui.exporter.ExportToClipboardAction; import org.jabref.gui.filelist.AttachFileAction; import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.gui.menus.ChangeEntryTypeMenu; -import org.jabref.gui.mergeentries.FetchAndMergeEntry; +import org.jabref.gui.mergeentries.MergeEntriesAction; +import org.jabref.gui.mergeentries.MergeWithFetchedEntryAction; +import org.jabref.gui.preview.CopyCitationAction; import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory; +import org.jabref.logic.citationstyle.CitationStyleOutputFormat; import org.jabref.logic.citationstyle.CitationStylePreviewLayout; import org.jabref.logic.citationstyle.PreviewLayout; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.SpecialField; -import org.jabref.model.entry.field.StandardField; import org.jabref.preferences.JabRefPreferences; +import org.jabref.preferences.PreferencesService; import org.jabref.preferences.PreviewPreferences; public class RightClickMenu { - public static ContextMenu create(BibEntryTableViewModel entry, KeyBindingRepository keyBindingRepository, BasePanel panel, DialogService dialogService) { + public static ContextMenu create(BibEntryTableViewModel entry, KeyBindingRepository keyBindingRepository, BasePanel panel, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { ContextMenu contextMenu = new ContextMenu(); ActionFactory factory = new ActionFactory(keyBindingRepository); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.COPY, new OldCommandWrapper(Actions.COPY, panel))); - contextMenu.getItems().add(createCopySubMenu(panel, factory, dialogService)); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.PASTE, new OldCommandWrapper(Actions.PASTE, panel))); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.CUT, new OldCommandWrapper(Actions.CUT, panel))); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.DELETE, new OldCommandWrapper(Actions.DELETE, panel))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.COPY, new EditAction(StandardActions.COPY, panel.frame(), stateManager))); + contextMenu.getItems().add(createCopySubMenu(panel, factory, dialogService, stateManager, preferencesService)); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, panel.frame(), stateManager))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.CUT, new EditAction(StandardActions.CUT, panel.frame(), stateManager))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.DELETE_ENTRY, new EditAction(StandardActions.DELETE_ENTRY, panel.frame(), stateManager))); contextMenu.getItems().add(new SeparatorMenuItem()); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new OldCommandWrapper(Actions.SEND_AS_EMAIL, panel))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.SEND_AS_EMAIL, new SendAsEMailAction(dialogService, stateManager))); contextMenu.getItems().add(new SeparatorMenuItem()); if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) { - contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, panel)); - contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, panel)); - contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, panel)); - contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, panel)); - contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, panel)); - contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, panel)); + // ToDo: SpecialField needs the active BasePanel to mark it as changed. + // Refactor BasePanel, should mark the BibDatabaseContext or the UndoManager as dirty instead! + contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.RANKING, factory, panel.frame(), dialogService, stateManager)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.RELEVANCE, factory, panel.frame(), dialogService, stateManager)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.QUALITY, factory, panel.frame(), dialogService, stateManager)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.getSpecialFieldSingleItem(SpecialField.PRINTED, factory, panel.frame(), dialogService, stateManager)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.PRIORITY, factory, panel.frame(), dialogService, stateManager)); + contextMenu.getItems().add(SpecialFieldMenuItemFactory.createSpecialFieldMenu(SpecialField.READ_STATUS, factory, panel.frame(), dialogService, stateManager)); } contextMenu.getItems().add(new SeparatorMenuItem()); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_FOLDER, getOpenFolderCommand(panel))); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, getOpenExternalFileCommand(panel))); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_URL, getOpenUrlCommand(panel))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, preferencesService))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_URL, new OpenUrlAction(dialogService, stateManager))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager))); contextMenu.getItems().add(new SeparatorMenuItem()); contextMenu.getItems().add(new ChangeEntryTypeMenu().getChangeEntryTypeMenu(entry.getEntry(), panel.getBibDatabaseContext(), panel.getUndoManager())); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, getFetchEntryData(panel))); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.ATTACH_FILE, new AttachFileAction(panel, dialogService))); - contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_ENTRIES, mergeEntries(panel))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_WITH_FETCHED_ENTRY, new MergeWithFetchedEntryAction(panel, dialogService, stateManager))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.ATTACH_FILE, new AttachFileAction(panel, dialogService, stateManager, preferencesService))); + // ToDo: Refactor BasePanel, see ahead. + contextMenu.getItems().add(factory.createMenuItem(StandardActions.MERGE_ENTRIES, new MergeEntriesAction(panel.frame(), dialogService, stateManager))); return contextMenu; } - private static OldCommandWrapper mergeEntries(BasePanel panel) { - OldCommandWrapper command = new OldCommandWrapper(Actions.MERGE_ENTRIES, panel); - command.setExecutable(panel.getMainTable().getSelectedEntries().size() == 2); - return command; - } - - private static OldCommandWrapper getFetchEntryData(BasePanel panel) { - OldCommandWrapper command = new OldCommandWrapper(Actions.MERGE_WITH_FETCHED_ENTRY, panel); - command.setExecutable(isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS, panel)); - return command; - } - - private static OldCommandWrapper getOpenUrlCommand(BasePanel panel) { - OldCommandWrapper command = new OldCommandWrapper(Actions.OPEN_URL, panel); - command.setExecutable(isFieldSetForSelectedEntry(StandardField.URL, panel) || isFieldSetForSelectedEntry(StandardField.DOI, panel)); - return command; - } - - private static OldCommandWrapper getOpenExternalFileCommand(BasePanel panel) { - OldCommandWrapper command = new OldCommandWrapper(Actions.OPEN_EXTERNAL_FILE, panel); - command.setExecutable(isFieldSetForSelectedEntry(StandardField.FILE, panel)); - return command; - } - - private static OldCommandWrapper getOpenFolderCommand(BasePanel panel) { - OldCommandWrapper command = new OldCommandWrapper(Actions.OPEN_FOLDER, panel); - command.setExecutable(isFieldSetForSelectedEntry(StandardField.FILE, panel)); - return command; - } - - private static Menu createCopySubMenu(BasePanel panel, ActionFactory factory, DialogService dialogService) { + private static Menu createCopySubMenu(BasePanel panel, ActionFactory factory, DialogService dialogService, StateManager stateManager, PreferencesService preferencesService) { Menu copySpecialMenu = factory.createMenu(StandardActions.COPY_MORE); - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_TITLE, new OldCommandWrapper(Actions.COPY_TITLE, panel))); - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY, new OldCommandWrapper(Actions.COPY_KEY, panel))); - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITE_KEY, new OldCommandWrapper(Actions.COPY_CITE_KEY, panel))); - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new OldCommandWrapper(Actions.COPY_KEY_AND_TITLE, panel))); - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new OldCommandWrapper(Actions.COPY_KEY_AND_LINK, panel))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_TITLE, new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, Globals.clipboardManager, preferencesService))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY, new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, Globals.clipboardManager, preferencesService))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITE_KEY, new CopyMoreAction(StandardActions.COPY_CITE_KEY, dialogService, stateManager, Globals.clipboardManager, preferencesService))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new CopyMoreAction(StandardActions.COPY_KEY_AND_TITLE, dialogService, stateManager, Globals.clipboardManager, preferencesService))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new CopyMoreAction(StandardActions.COPY_KEY_AND_LINK, dialogService, stateManager, Globals.clipboardManager, preferencesService))); // the submenu will behave dependent on what style is currently selected (citation/preview) PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences(); PreviewLayout style = previewPreferences.getCurrentPreviewStyle(); if (style instanceof CitationStylePreviewLayout) { - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_HTML, new OldCommandWrapper(Actions.COPY_CITATION_HTML, panel))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_HTML, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.clipboardManager, previewPreferences))); Menu copyCitationMenu = factory.createMenu(StandardActions.COPY_CITATION_MORE); - copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_TEXT, new OldCommandWrapper(Actions.COPY_CITATION_TEXT, panel))); - copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_RTF, new OldCommandWrapper(Actions.COPY_CITATION_RTF, panel))); - copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_ASCII_DOC, new OldCommandWrapper(Actions.COPY_CITATION_ASCII_DOC, panel))); - copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_XSLFO, new OldCommandWrapper(Actions.COPY_CITATION_XSLFO, panel))); + copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_TEXT, new CopyCitationAction(CitationStyleOutputFormat.TEXT, dialogService, stateManager, Globals.clipboardManager, previewPreferences))); + copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_RTF, new CopyCitationAction(CitationStyleOutputFormat.RTF, dialogService, stateManager, Globals.clipboardManager, previewPreferences))); + copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_ASCII_DOC, new CopyCitationAction(CitationStyleOutputFormat.ASCII_DOC, dialogService, stateManager, Globals.clipboardManager, previewPreferences))); + copyCitationMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_XSLFO, new CopyCitationAction(CitationStyleOutputFormat.XSL_FO, dialogService, stateManager, Globals.clipboardManager, previewPreferences))); copySpecialMenu.getItems().add(copyCitationMenu); } else { - copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new OldCommandWrapper(Actions.COPY_CITATION_HTML, panel))); + copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.clipboardManager, previewPreferences))); } copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.EXPORT_TO_CLIPBOARD, new ExportToClipboardAction(panel, dialogService))); return copySpecialMenu; } - - private static boolean isFieldSetForSelectedEntry(Field field, BasePanel panel) { - return isAnyFieldSetForSelectedEntry(Collections.singletonList(field), panel); - } - - private static boolean isAnyFieldSetForSelectedEntry(List fields, BasePanel panel) { - if (panel.getMainTable().getSelectedEntries().size() == 1) { - BibEntry entry = panel.getMainTable().getSelectedEntries().get(0); - return !Collections.disjoint(fields, entry.getFields()); - } - return false; - } } diff --git a/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java b/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java new file mode 100644 index 00000000000..663fd5be697 --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java @@ -0,0 +1,50 @@ +package org.jabref.gui.maintable; + +import java.io.IOException; +import java.util.List; + +import javafx.beans.binding.BooleanExpression; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.desktop.JabRefDesktop; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.ExternalLinkCreator; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +import static org.jabref.gui.actions.ActionHelper.isFieldSetForSelectedEntry; +import static org.jabref.gui.actions.ActionHelper.needsEntriesSelected; + +public class SearchShortScienceAction extends SimpleCommand { + private final DialogService dialogService; + private final StateManager stateManager; + + public SearchShortScienceAction(DialogService dialogService, StateManager stateManager) { + this.dialogService = dialogService; + this.stateManager = stateManager; + + BooleanExpression fieldIsSet = isFieldSetForSelectedEntry(StandardField.TITLE, stateManager); + this.executable.bind(needsEntriesSelected(1, stateManager).and(fieldIsSet)); + } + + @Override + public void execute() { + stateManager.getActiveDatabase().ifPresent(databaseContext -> { + final List bibEntries = stateManager.getSelectedEntries(); + + if (bibEntries.size() != 1) { + dialogService.notify(Localization.lang("This operation requires exactly one item to be selected.")); + return; + } + ExternalLinkCreator.getShortScienceSearchURL(bibEntries.get(0)).ifPresent(url -> { + try { + JabRefDesktop.openExternalViewer(databaseContext, url, StandardField.URL); + } catch (IOException ex) { + dialogService.showErrorDialogAndWait(Localization.lang("Unable to open ShortScience."), ex); + } + }); + }); + } +} diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java index baca427159e..7ccc29e2515 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeEntriesAction.java @@ -4,43 +4,47 @@ import java.util.List; import java.util.Optional; -import org.jabref.gui.BasePanel; +import org.jabref.Globals; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableInsertEntries; import org.jabref.gui.undo.UndoableRemoveEntries; import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import static org.jabref.gui.actions.ActionHelper.needsDatabase; - public class MergeEntriesAction extends SimpleCommand { - private final JabRefFrame jabRefFrame; + private final JabRefFrame frame; private final DialogService dialogService; + private final StateManager stateManager; - public MergeEntriesAction(JabRefFrame jabRefFrame, StateManager stateManager) { - this.jabRefFrame = jabRefFrame; - this.dialogService = jabRefFrame.getDialogService(); + public MergeEntriesAction(JabRefFrame frame, DialogService dialogService, StateManager stateManager) { + this.frame = frame; + this.dialogService = dialogService; + this.stateManager = stateManager; - this.executable.bind(needsDatabase(stateManager)); + this.executable.bind(ActionHelper.needsEntriesSelected(2, stateManager)); } @Override public void execute() { - BasePanel basePanel = jabRefFrame.getCurrentBasePanel(); + if (stateManager.getActiveDatabase().isEmpty()) { + return; + } + BibDatabaseContext databaseContext = stateManager.getActiveDatabase().get(); // Check if there are two entries selected - List selectedEntries = basePanel.getSelectedEntries(); + List selectedEntries = stateManager.getSelectedEntries(); if (selectedEntries.size() != 2) { // Inform the user to select entries first. dialogService.showInformationDialogAndWait( Localization.lang("Merge entries"), Localization.lang("You have to choose exactly two entries to merge.")); - return; } @@ -52,17 +56,20 @@ public void execute() { dlg.setTitle(Localization.lang("Merge entries")); Optional mergedEntry = dlg.showAndWait(); if (mergedEntry.isPresent()) { - basePanel.insertEntry(mergedEntry.get()); + // ToDo: BibDatabase::insertEntry does not contain logic to mark the BasePanel as changed and to mark + // entries with a timestamp, only BasePanel::insertEntry does. Workaround for the moment is to get the + // BasePanel from the constructor injected JabRefFrame. Should be refactored and extracted! + frame.getCurrentBasePanel().insertEntry(mergedEntry.get()); // Create a new entry and add it to the undo stack // Remove the other two entries and add them to the undo stack (which is not working...) NamedCompound ce = new NamedCompound(Localization.lang("Merge entries")); - ce.addEdit(new UndoableInsertEntries(basePanel.getDatabase(), mergedEntry.get())); + ce.addEdit(new UndoableInsertEntries(databaseContext.getDatabase(), mergedEntry.get())); List entriesToRemove = Arrays.asList(one, two); - ce.addEdit(new UndoableRemoveEntries(basePanel.getDatabase(), entriesToRemove)); - basePanel.getDatabase().removeEntries(entriesToRemove); + ce.addEdit(new UndoableRemoveEntries(databaseContext.getDatabase(), entriesToRemove)); + databaseContext.getDatabase().removeEntries(entriesToRemove); ce.end(); - basePanel.getUndoManager().addEdit(ce); + Globals.undoManager.addEdit(ce); // ToDo: Rework UndoManager and extract Globals dialogService.notify(Localization.lang("Merged entries")); } else { diff --git a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java index 2b2edf17017..e67c42b0df2 100644 --- a/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java +++ b/src/main/java/org/jabref/gui/mergeentries/MergeWithFetchedEntryAction.java @@ -3,32 +3,38 @@ import org.jabref.Globals; import org.jabref.gui.BasePanel; import org.jabref.gui.DialogService; -import org.jabref.gui.actions.BaseAction; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.ActionHelper; +import org.jabref.gui.actions.SimpleCommand; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.OrFields; import org.jabref.model.entry.field.StandardField; -public class MergeWithFetchedEntryAction implements BaseAction { +public class MergeWithFetchedEntryAction extends SimpleCommand { private final BasePanel basePanel; private final DialogService dialogService; + private final StateManager stateManager; - public MergeWithFetchedEntryAction(BasePanel basePanel, DialogService dialogService) { + public MergeWithFetchedEntryAction(BasePanel basePanel, DialogService dialogService, StateManager stateManager) { this.basePanel = basePanel; this.dialogService = dialogService; + this.stateManager = stateManager; + + this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager) + .and(ActionHelper.isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS, stateManager))); } @Override - public void action() { - if (basePanel.getMainTable().getSelectedEntries().size() == 1) { - BibEntry originalEntry = basePanel.getMainTable().getSelectedEntries().get(0); - new FetchAndMergeEntry(basePanel, Globals.TASK_EXECUTOR).fetchAndMerge(originalEntry); - } else { + public void execute() { + if (stateManager.getSelectedEntries().size() != 1) { dialogService.showInformationDialogAndWait( Localization.lang("Merge entry with %0 information", new OrFields(StandardField.DOI, StandardField.ISBN, StandardField.EPRINT).getDisplayName()), Localization.lang("This operation requires exactly one item to be selected.")); - } + + BibEntry originalEntry = stateManager.getSelectedEntries().get(0); + new FetchAndMergeEntry(basePanel, Globals.TASK_EXECUTOR).fetchAndMerge(originalEntry); } } diff --git a/src/main/java/org/jabref/gui/preferences/AppearanceTab.fxml b/src/main/java/org/jabref/gui/preferences/AppearanceTab.fxml index ed2f9605c5e..e7e95fc0e5a 100644 --- a/src/main/java/org/jabref/gui/preferences/AppearanceTab.fxml +++ b/src/main/java/org/jabref/gui/preferences/AppearanceTab.fxml @@ -7,8 +7,8 @@ - + @@ -18,9 +18,9 @@