Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Fix theme selection when theme name contains an accented character

AuthorDaveJarvis <email>
Date2023-01-28 12:10:53 GMT-0800
Commit93b92d6cab7d33a55c052159f7e28de785a1f4d6
Parent4174a6f
Delta71 lines added, 26 lines removed, 45-line increase
src/main/java/com/keenwrite/ui/dialogs/ExportDialog.java
import javafx.stage.Stage;
import javafx.stage.Window;
+import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Properties;
-import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
import static java.lang.Math.max;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.text.Normalizer.*;
+import static java.text.Normalizer.Form.*;
import static javafx.application.Platform.runLater;
import static javafx.geometry.Pos.CENTER;
*/
public final class ExportDialog extends AbstractDialog<ExportSettings> {
+ private static final String UNCRITIC = "\\p{InCombiningDiacriticalMarks}+";
+
+ private record Theme( Path path, String name ) implements Comparable<Theme> {
+
+ /**
+ * Answers whether the given theme directory name matches the theme name
+ * that the user selected.
+ *
+ * @param themeDir The user-selected directory to compare with the
+ * corresponding path of this {@link Theme}.
+ * @return {@code true} if the given directory matches the ending portion
+ * of the {@link Path} associated with this {@link Theme} instance.
+ */
+ public boolean matches( final String themeDir ) {
+ final var normalized = normalize( themeDir, NFKD );
+ final var name = normalized.replaceAll( UNCRITIC, "" );
+ final var path = path().getFileName().toString();
+
+ return path.equalsIgnoreCase( name );
+ }
+
+ /**
+ * Returns the theme's display name.
+ *
+ * @return The name of the theme presented to users.
+ */
+ @Override
+ public String toString() {
+ return abbreviate( name(), THEME_NAME_LENGTH );
+ }
+
+ @Override
+ public int compareTo( @NotNull final ExportDialog.Theme o ) {
+ return name().compareTo( o.name() );
+ }
+ }
+
private final File mThemes;
private final ExportSettings mSettings;
private GridPane mPane;
- private ComboBox<String> mComboBox;
+ private ComboBox<Theme> mComboBox;
private TextField mChapters;
private final boolean mMissingThemes;
}
- initComboBox( mComboBox, mSettings, readThemes( themesDir ) );
+ final var previousTheme = mSettings.themeProperty().get();
+
+ initComboBox( mComboBox, previousTheme, themes );
mPane.add( createLabel( "Dialog.typesetting.settings.theme" ), 0, 1 );
if( result.isPresent() ) {
final var theme = mComboBox.getSelectionModel().getSelectedItem();
- mSettings.themeProperty().setValue( theme.toLowerCase() );
+ final var path = theme.path().getFileName().toString();
+ mSettings.themeProperty().setValue( path );
return true;
private void initComboBox(
- final ComboBox<String> comboBox,
- final ExportSettings settings,
- final TreeMap<String, String> choices
+ final ComboBox<Theme> comboBox,
+ final String previousTheme,
+ final List<Theme> choices
) {
assert comboBox != null;
- assert settings != null;
+ assert previousTheme != null;
assert choices != null;
- final var selection = new String[]{""};
- final var theme = settings.themeProperty().get();
+ final var items = comboBox.getItems();
+ items.clear();
+ items.addAll( choices );
// Set the selected item to user's settings value.
- for( final var key : choices.keySet() ) {
- if( key.equalsIgnoreCase( theme ) ) {
- selection[ 0 ] = key;
+ for( final var choice : choices ) {
+ if( choice.matches( previousTheme ) ) {
+ comboBox.getSelectionModel().select(
+ items.get( max( items.indexOf( choice ), 0 ) )
+ );
+
break;
}
}
-
- final var items = comboBox.getItems();
- items.addAll( choices.keySet() );
- comboBox.getSelectionModel().select(
- items.get( max( items.indexOf( selection[ 0 ] ), 0 ) )
- );
}
- private TreeMap<String, String> readThemes( final File themesDir ) {
+ private List<Theme> readThemes( final File themesDir ) {
try {
// List themes in alphabetical order (human-readable by directory name).
- final var choices = new TreeMap<String, String>();
+ final var choices = new LinkedList<Theme>();
// Populate the choices with themes detected on the system.
walk( themesDir.toPath(), "**/theme.properties", path -> {
try {
- final var displayed = readThemeName( path );
- final var themeName = path.getParent().toFile().getName();
- choices.put( abbreviate( displayed, THEME_NAME_LENGTH ), themeName );
+ final var themeName = readThemeName( path );
+ final var themePath = path.getParent();
+ choices.add( new Theme( themePath, themeName ) );
} catch( final Exception ex ) {
clue( "Main.status.error.theme.name", path );
}
} );
+
+ Collections.sort( choices );
return choices;
} catch( final Exception ex ) {
clue( ex );
}
- return new TreeMap<>();
+ return Collections.emptyList();
}
- private ComboBox<String> createComboBox() {
+ private ComboBox<Theme> createComboBox() {
return new ComboBox<>();
}