Skip to content

Commit

Permalink
Replace <p> in localization by \n (#7279)
Browse files Browse the repository at this point in the history
Co-authored-by: Christoph <siedlerkiller@gmail.com>
Co-authored-by: Carl Christian Snethlage <cc.snethlage@gmail.com>
  • Loading branch information
3 people authored Jun 7, 2021
1 parent bfe2679 commit ffd623d
Show file tree
Hide file tree
Showing 18 changed files with 495 additions and 330 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
- The export to MS Office XML now uses the month name for the field `Month` instead of the two digit number [forum#2685](https://discourse.jabref.org/t/export-month-as-text-not-number/2685)
- We reintroduced missing default keybindings for new entries. [#7346](https://github.com/JabRef/jabref/issues/7346) [#7439](https://github.com/JabRef/jabref/issues/7439)
- Lists of available fields are now sorted alphabetically. [#7716](https://github.com/JabRef/jabref/issues/7716)
- The tooltip of the search field explaining the search is always shown. [#7279](https://github.com/JabRef/jabref/pull/7279)
- We rewrote the ACM fetcher to adapt to the new interface. [#5804](https://github.com/JabRef/jabref/issues/5804)
- We moved the select/collapse buttons in the unlinked files dialog into a context menu. [#7383](https://github.com/JabRef/jabref/issues/7383)

Expand Down
67 changes: 29 additions & 38 deletions src/main/java/org/jabref/gui/search/GlobalSearchBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ public class GlobalSearchBar extends HBox {
private final ToggleButton caseSensitiveButton;
private final ToggleButton regularExpressionButton;
// private final Button searchModeButton;
private final Tooltip searchFieldTooltip = new Tooltip();
private final Label currentResults = new Label("");
private final Tooltip tooltip = new Tooltip();

private final StateManager stateManager;
private final PreferencesService preferencesService;
Expand All @@ -101,9 +101,9 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, Preferences
// fits the standard "found x entries"-message thus hinders the searchbar to jump around while searching if the frame width is too small
currentResults.setPrefWidth(150);

tooltip.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
tooltip.setMaxHeight(10);
searchField.setTooltip(null);
searchField.setTooltip(searchFieldTooltip);
searchFieldTooltip.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
searchFieldTooltip.setMaxHeight(10);
updateHintVisibility();

KeyBindingRepository keyBindingRepository = Globals.getKeyPrefs();
Expand Down Expand Up @@ -179,14 +179,7 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager, Preferences
private void initSearchModifierButtons() {
regularExpressionButton.setSelected(searchPreferences.isRegularExpression());
regularExpressionButton.setTooltip(new Tooltip(Localization.lang("regular expression")));
regularExpressionButton.setCursor(Cursor.DEFAULT);
regularExpressionButton.setMinHeight(28);
regularExpressionButton.setMaxHeight(28);
regularExpressionButton.setMinWidth(28);
regularExpressionButton.setMaxWidth(28);
regularExpressionButton.setPadding(new Insets(1.0));
regularExpressionButton.managedProperty().bind(searchField.editableProperty());
regularExpressionButton.visibleProperty().bind(searchField.editableProperty());
initSearchModifierButton(regularExpressionButton);
regularExpressionButton.setOnAction(event -> {
searchPreferences = searchPreferences.withRegularExpression(regularExpressionButton.isSelected());
preferencesService.storeSearchPreferences(searchPreferences);
Expand All @@ -195,14 +188,7 @@ private void initSearchModifierButtons() {

caseSensitiveButton.setSelected(searchPreferences.isCaseSensitive());
caseSensitiveButton.setTooltip(new Tooltip(Localization.lang("Case sensitive")));
caseSensitiveButton.setCursor(Cursor.DEFAULT);
caseSensitiveButton.setMinHeight(28);
caseSensitiveButton.setMaxHeight(28);
caseSensitiveButton.setMinWidth(28);
caseSensitiveButton.setMaxWidth(28);
caseSensitiveButton.setPadding(new Insets(1.0));
caseSensitiveButton.managedProperty().bind(searchField.editableProperty());
caseSensitiveButton.visibleProperty().bind(searchField.editableProperty());
initSearchModifierButton(caseSensitiveButton);
caseSensitiveButton.setOnAction(event -> {
searchPreferences = searchPreferences.withCaseSensitive(caseSensitiveButton.isSelected());
preferencesService.storeSearchPreferences(searchPreferences);
Expand All @@ -227,6 +213,17 @@ private void initSearchModifierButtons() {
}); */
}

private void initSearchModifierButton(ToggleButton searchButton) {
searchButton.setCursor(Cursor.DEFAULT);
searchButton.setMinHeight(28);
searchButton.setMaxHeight(28);
searchButton.setMinWidth(28);
searchButton.setMaxWidth(28);
searchButton.setPadding(new Insets(1.0));
searchButton.managedProperty().bind(searchField.editableProperty());
searchButton.visibleProperty().bind(searchField.editableProperty());
}

/**
* Focuses the search field if it is not focused.
*/
Expand All @@ -243,7 +240,7 @@ public void performSearch() {
// An empty search field should cause the search to be cleared.
if (searchField.getText().isEmpty()) {
currentResults.setText("");
setHintTooltip(null);
setSearchFieldHintTooltip(null);
stateManager.clearSearchQuery();
return;
}
Expand Down Expand Up @@ -324,34 +321,28 @@ private void updateResults(int matched, TextFlow description, boolean grammarBas
// searchIcon.setIcon(IconTheme.JabRefIcon.SEARCH.getIcon());
}

setHintTooltip(description);
setSearchFieldHintTooltip(description);
}

private void setHintTooltip(TextFlow description) {
private void setSearchFieldHintTooltip(TextFlow description) {
if (preferencesService.getGeneralPreferences().shouldShowAdvancedHints()) {
String genericDescription = Localization.lang("Hint: To search specific fields only, enter for example:<p><tt>author=smith and title=electrical</tt>");
genericDescription = genericDescription.replace("<p>", "\n");
List<Text> genericDescriptionTexts = TooltipTextUtil.formatToTexts(genericDescription, new TooltipTextUtil.TextReplacement("<tt>author=smith and title=electrical</tt>", "author=smith and title=electrical", TooltipTextUtil.TextType.MONOSPACED));
String genericDescription = Localization.lang("Hint:\n\nTo search all fields for <b>Smith</b>, enter:\n<tt>smith</tt>\n\nTo search the field <b>author</b> for <b>Smith</b> and the field <b>title</b> for <b>electrical</b>, enter:\n<tt>author=Smith and title=electrical</tt>");
List<Text> genericDescriptionTexts = TooltipTextUtil.createTextsFromHtml(genericDescription);

if (description != null) {
description.getChildren().add(new Text("\n\n"));
description.getChildren().addAll(genericDescriptionTexts);
tooltip.setGraphic(description);
} else {
if (description == null) {
TextFlow emptyHintTooltip = new TextFlow();
emptyHintTooltip.getChildren().setAll(genericDescriptionTexts);
tooltip.setGraphic(emptyHintTooltip);
searchFieldTooltip.setGraphic(emptyHintTooltip);
} else {
description.getChildren().add(new Text("\n\n"));
description.getChildren().addAll(genericDescriptionTexts);
searchFieldTooltip.setGraphic(description);
}
}
}

public void updateHintVisibility() {
if (preferencesService.getGeneralPreferences().shouldShowAdvancedHints()) {
searchField.setTooltip(tooltip);
} else {
searchField.setTooltip(null);
}
setHintTooltip(null);
setSearchFieldHintTooltip(null);
}

public void setSearchTerm(String searchTerm) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ public TextFlow getDescription() {
}

textList.add(getCaseSensitiveDescription());
textList.add(TooltipTextUtil.createText("\n\n" +
Localization.lang("Hint: To search specific fields only, enter for example:")));
textList.add(TooltipTextUtil.createText(" author=smith and title=electrical", TooltipTextUtil.TextType.MONOSPACED));

TextFlow searchDescription = new TextFlow();
searchDescription.getChildren().setAll(textList);
Expand Down
77 changes: 67 additions & 10 deletions src/main/java/org/jabref/gui/util/TooltipTextUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javafx.scene.text.Text;

Expand All @@ -11,6 +13,13 @@
*/
public class TooltipTextUtil {

// (?s) tells Java that "." also matches the newline character
// (?<...>...) are named groups in Java regular expressions: https://stackoverflow.com/a/415635/873282
// .*? tells to match non-greedy (see https://stackoverflow.com/q/7124778/873282 for details)
private static final Pattern TT_TEXT = Pattern.compile("(?s)(?<before>.*?)<tt>(?<in>.*?)</tt>");

private static final Pattern B_TEXT = Pattern.compile("(?s)(?<before>.*?)<b>(?<in>.*?)</b>");

public enum TextType {
NORMAL, BOLD, ITALIC, MONOSPACED
}
Expand All @@ -37,19 +46,52 @@ public static Text createText(String textString) {
return createText(textString, TextType.NORMAL);
}

public static String textToHTMLString(Text text) {
String textString = text.getText();
textString = textString.replace("\n", "<br>");
if (text.getStyleClass().toString().contains("tooltip-text-monospaced")) {
textString = String.format("<kbd>%s</kbd>", textString);
/**
* Creates a list of Text elements respecting <code>tt</code> and <code>b</code> markers.
* Nesting of these markers is not possible.
*/
public static List<Text> createTextsFromHtml(String htmlString) {
List<Text> result = new ArrayList<>();

Matcher matcher = TT_TEXT.matcher(htmlString);
int lastMatchPos = 0;
while (matcher.find()) {
lastMatchPos = matcher.end();
String before = matcher.group("before");
if (!before.isBlank()) {
result.addAll(convertHtmlBold(before));
}
String in = matcher.group("in");
result.add(TooltipTextUtil.createText(in, TooltipTextUtil.TextType.MONOSPACED));
}
if (text.getStyleClass().toString().contains("tooltip-text-bold")) {
textString = String.format("<b>%s</b>", textString);
if (lastMatchPos < htmlString.length()) {
String remaining = htmlString.substring(lastMatchPos);
result.addAll(convertHtmlBold(remaining));
}
if (text.getStyleClass().toString().contains("tooltip-text-italic")) {
textString = String.format("<i>%s</i>", textString);

return result;
}

private static List<Text> convertHtmlBold(String htmlString) {
List<Text> result = new ArrayList<>();

Matcher matcher = B_TEXT.matcher(htmlString);
int lastMatchPos = 0;
while (matcher.find()) {
lastMatchPos = matcher.end();
String before = matcher.group("before");
if (!before.isBlank()) {
result.add(TooltipTextUtil.createText(before));
}
String in = matcher.group("in");
result.add(TooltipTextUtil.createText(in, TextType.BOLD));
}
return textString;
if (lastMatchPos < htmlString.length()) {
String remaining = htmlString.substring(lastMatchPos);
result.add(TooltipTextUtil.createText(remaining));
}

return result;
}

/**
Expand Down Expand Up @@ -105,4 +147,19 @@ public TextReplacement(String toReplace, String replacement, TooltipTextUtil.Tex
this.textType = textType;
}
}

public static String textToHtmlString(Text text) {
String textString = text.getText();
textString = textString.replace("\n", "<br>");
if (text.getStyleClass().toString().contains("tooltip-text-monospaced")) {
textString = String.format("<tt>%s</tt>", textString);
}
if (text.getStyleClass().toString().contains("tooltip-text-bold")) {
textString = String.format("<b>%s</b>", textString);
}
if (text.getStyleClass().toString().contains("tooltip-text-italic")) {
textString = String.format("<i>%s</i>", textString);
}
return textString;
}
}
14 changes: 8 additions & 6 deletions src/main/java/org/jabref/logic/l10n/Localization.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Optional;
Expand Down Expand Up @@ -118,17 +119,18 @@ private static void createResourceBundles(Locale locale) {
}

/**
* Helper function to create a HashMap from the key/value pairs of a bundle.
* Helper function to create a Map from the key/value pairs of a bundle.
*
* @param baseBundle JabRef language bundle with keys and values for translations.
* @return Lookup map for the baseBundle.
*/
private static HashMap<String, String> createLookupMap(ResourceBundle baseBundle) {
private static Map<String, String> createLookupMap(ResourceBundle baseBundle) {
final ArrayList<String> baseKeys = Collections.list(baseBundle.getKeys());
return new HashMap<>(baseKeys.stream().collect(
Collectors.toMap(
key -> new LocalizationKey(key).getTranslationValue(),
key -> new LocalizationKey(baseBundle.getString(key)).getTranslationValue())
// not required to unescape content, because that is already done by the ResourceBundle itself
key -> key,
key -> baseBundle.getString(key))
));
}

Expand Down Expand Up @@ -157,9 +159,9 @@ private static String lookup(LocalizationBundle bundle, String key, String... pa
*/
private static class LocalizationBundle extends ResourceBundle {

private final HashMap<String, String> lookup;
private final Map<String, String> lookup;

LocalizationBundle(HashMap<String, String> lookupMap) {
LocalizationBundle(Map<String, String> lookupMap) {
lookup = lookupMap;
}

Expand Down
58 changes: 46 additions & 12 deletions src/main/java/org/jabref/logic/l10n/LocalizationKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,61 @@

import java.util.Objects;

/**
* Model for a localization to translate. The key is the English text.
*/
public class LocalizationKey {

private final String key;
private final String escapedPropertyKey;

public LocalizationKey(String key) {
this.key = Objects.requireNonNull(key);
/**
* @param key plain key - no escaping. E.g., "Copy \cite{key}" or "Newline follows\nsecond line" are valid parameters.
*/
private LocalizationKey(String key) {
this.key = key;
// space, #, !, = and : are not allowed in properties file keys
// # and ! are only disallowed at the beginning of the key but easier to escape every instance
this.escapedPropertyKey = key
.replace("\n", "\\n")
.replace(" ", "\\ ")
.replace("#", "\\#")
.replace("!", "\\!")
.replace("=", "\\=")
.replace(":", "\\:");
}

public String getPropertiesKeyUnescaped() {
// space, #, !, = and : are not allowed in properties file keys
return this.key;
/**
* @param key plain key - no escaping. E.g., "Copy \cite{key}" or "Newline follows\nsecond line" are valid parameters.
*/
public static LocalizationKey fromKey(String key) {
return new LocalizationKey(Objects.requireNonNull(key));
}

public String getPropertiesKey() {
// space, #, !, = and : are not allowed in properties file keys (# and ! only at the beginning of the key but easier to escape every instance
return this.key.replace(" ", "\\ ").replace("#", "\\#").replace("!", "\\!").replace("=", "\\=")
.replace(":", "\\:").replace("\\\\", "\\");
public static LocalizationKey fromEscapedJavaString(String key) {
// "\n" in the string is an escaped newline. That needs to be kept.
// "\\" in the string can stay --> needs to be kept
return new LocalizationKey(Objects.requireNonNull(key));
}

public String getTranslationValue() {
return this.key.replace("\\ ", " ").replace("\\#", "#").replace("\\!", "!").replace("\\=", "=").replace("\\:",
":");
/*
public static LocalizationKey fromPropertyKey(String key) {
String propertyKey = key;
// we ne need to unescape the escaped characters (see org.jabref.logic.l10n.LocalizationKey.LocalizationKey)
String javaCodeKey = key.replaceAll("\\\\([ #!=:])", "$1");
return new LocalizationKey(javaCodeKey, propertyKey);
}
*/
public String getEscapedPropertiesKey() {
return this.escapedPropertyKey;
}

public String getValueForEnglishPropertiesFile() {
// Newline needs to be escaped
return this.key.replace("\n", "\\n");
}

public String getKey() {
return this.key;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ public class LocalizationKeyParams {
private final List<String> params;

public LocalizationKeyParams(String key, String... params) {
this.key = new LocalizationKey(key);
this.key = LocalizationKey.fromKey(key);
this.params = Arrays.asList(params);
if (this.params.size() > 10) {
throw new IllegalStateException("Translations can only have at most 10 parameters");
}
}

public String replacePlaceholders() {
String translation = key.getTranslationValue();
String translation = key.getKey();

for (int i = 0; i < params.size(); i++) {
String param = params.get(i);
Expand Down
Loading

0 comments on commit ffd623d

Please sign in to comment.