Dave Jarvis' Repositories

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

Renamed keys, add ring buffer

AuthorDaveJarvis <email>
Date2021-12-18 13:05:49 GMT-0800
Commitbbc2a23a5e0f4edfdf220832f63d9785d509db45
Parent73f3017
Delta665 lines added, 611 lines removed, 54-line increase
src/test/java/com/keenwrite/util/CircularQueueTest.java
+package com.keenwrite.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests the {@link CircularQueue} class.
+ */
+public class CircularQueueTest {
+
+ /**
+ * Exercises the circularity aspect of the {@link CircularQueue}.
+ * Confirms that the elements added can be subsequently overwritten.
+ * This also checks that peek and remove functionality work as expected.
+ */
+ @Test
+ public void test_Add_ExceedMaxCapacity_FirstElementOverwritten() {
+ final var CAPACITY = 5;
+ final var OVERWRITE = 17;
+ final var ELEMENTS = CAPACITY + OVERWRITE;
+ final var queue = createQueue( CAPACITY, ELEMENTS );
+
+ assertEquals( CAPACITY, queue.size() );
+
+ for( int i = 0; i < CAPACITY; i++ ) {
+ final var expected =
+ ELEMENTS - ((((OVERWRITE - CAPACITY - 1) - i) % CAPACITY) + 1);
+
+ assertEquals( expected, queue.peek() );
+ assertEquals( expected, queue.remove() );
+ }
+ }
+
+ /**
+ * Tests iterating over all elements in the {@link CircularQueue}.
+ */
+ @Test
+ public void test_Iterate_FullQueue_AllElementsNavigated() {
+ final var CAPACITY = 101;
+ final var queue = createQueue( CAPACITY, CAPACITY );
+ int actualCount = 0;
+
+ for( final var ignored : queue ) {
+ actualCount++;
+ }
+
+ assertEquals( CAPACITY, actualCount );
+ }
+
+ /**
+ * Tests iterating over {@link CircularQueue} where some elements,
+ * starting at an arbitrary offset, have been removed.
+ */
+ @Test
+ public void test_Iterate_PartialQueue_AllElementsNavigated() {
+ final var CAPACITY = 31;
+ final var OVERWRITE = CAPACITY / 2;
+ final var queue = createQueue( CAPACITY, CAPACITY + OVERWRITE );
+ var actualCount = 0;
+
+ for( int i = 0; i < OVERWRITE; i++ ) {
+ queue.remove();
+ }
+
+ for( final var ignored : queue ) {
+ actualCount++;
+ }
+
+ assertEquals( CAPACITY - OVERWRITE, actualCount );
+ }
+
+ /**
+ * Creates a new, pre-populated {@link CircularQueue} instance.
+ *
+ * @param capacity The maximum number of elements before overwriting.
+ * @param count The number of elements to pre-populate the queue.
+ * @return A new {@link CircularQueue} pre-populated with ascending,
+ * consecutive values.
+ */
+ private static CircularQueue<Integer> createQueue(
+ final int capacity, final int count ) {
+ final var queue = new CircularQueue<Integer>( capacity );
+
+ for( int i = 0; i < count; i++ ) {
+ queue.add( i );
+ }
+
+ return queue;
+ }
+}
src/main/java/com/keenwrite/processors/XhtmlProcessor.java
import static com.keenwrite.events.StatusEvent.clue;
import static com.keenwrite.io.HttpFacade.httpGet;
-import static com.keenwrite.preferences.WorkspaceKeys.*;
+import static com.keenwrite.preferences.AppKeys.*;
import static com.keenwrite.processors.text.TextReplacementFactory.replace;
import static com.keenwrite.util.ProtocolScheme.getProtocol;
private String getImagePath() {
- return getWorkspace().toFile( KEY_IMAGES_DIR ).toString();
+ return getWorkspace().asFile( KEY_IMAGES_DIR ).toString();
}
private String getImageOrder() {
- return getWorkspace().toString( KEY_IMAGES_ORDER );
+ return getWorkspace().getString( KEY_IMAGES_ORDER );
}
*/
private boolean curl() {
- return getWorkspace().toBoolean( KEY_TYPESET_TYPOGRAPHY_QUOTES );
+ return getWorkspace().getBoolean( KEY_TYPESET_TYPOGRAPHY_QUOTES );
}
src/main/java/com/keenwrite/preferences/PreferencesController.java
import static com.keenwrite.preferences.LocaleProperty.localeListProperty;
import static com.keenwrite.preferences.SkinProperty.skinListProperty;
-import static com.keenwrite.preferences.WorkspaceKeys.*;
+import static com.keenwrite.preferences.AppKeys.*;
import static javafx.scene.control.ButtonType.CANCEL;
import static javafx.scene.control.ButtonType.OK;
src/main/java/com/keenwrite/preferences/Workspace.java
import static com.keenwrite.constants.Constants.*;
import static com.keenwrite.events.StatusEvent.clue;
-import static com.keenwrite.preferences.WorkspaceKeys.*;
-import static java.lang.String.valueOf;
-import static java.lang.System.getProperty;
-import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
-import static java.util.Map.entry;
-import static javafx.application.Platform.runLater;
-import static javafx.collections.FXCollections.observableArrayList;
-import static javafx.collections.FXCollections.observableSet;
-
-/**
- * Responsible for defining behaviours for separate projects. A workspace has
- * the ability to save and restore a session, including the window dimensions,
- * tab setup, files, and user preferences.
- * <p>
- * The configuration must support hierarchical (nested) configuration nodes
- * to persist the user interface state. Although possible with a flat
- * configuration file, it's not nearly as simple or elegant.
- * </p>
- * <p>
- * Neither JSON nor HOCON support schema validation and versioning, which makes
- * XML the more suitable configuration file format. Schema validation and
- * versioning provide future-proofing and ease of reading and upgrading previous
- * versions of the configuration file.
- * </p>
- * <p>
- * Persistent preferences may be set directly by the user or indirectly by
- * the act of using the application.
- * </p>
- * <p>
- * Note the following definitions:
- * </p>
- * <dl>
- * <dt>File</dt>
- * <dd>References a file name (no path), path, or directory.</dd>
- * <dt>Path</dt>
- * <dd>Fully qualified file name, which includes all parent directories.</dd>
- * <dt>Dir</dt>
- * <dd>Directory without file name ({@link File#isDirectory()} is true).</dd>
- * </dl>
- */
-public final class Workspace {
- private final Map<Key, Property<?>> VALUES = Map.ofEntries(
- entry( KEY_META_VERSION, asStringProperty( getVersion() ) ),
- entry( KEY_META_NAME, asStringProperty( "default" ) ),
-
- entry( KEY_DOC_TITLE, asStringProperty( "title" ) ),
- entry( KEY_DOC_AUTHOR, asStringProperty( getProperty( "user.name" ) ) ),
- entry( KEY_DOC_BYLINE, asStringProperty( getProperty( "user.name" ) ) ),
- entry( KEY_DOC_ADDRESS, asStringProperty( "" ) ),
- entry( KEY_DOC_PHONE, asStringProperty( "" ) ),
- entry( KEY_DOC_EMAIL, asStringProperty( "" ) ),
- entry( KEY_DOC_KEYWORDS, asStringProperty( "science, nature" ) ),
- entry( KEY_DOC_COPYRIGHT, asStringProperty( getYear() ) ),
- entry( KEY_DOC_DATE, asStringProperty( getDate() ) ),
-
- entry( KEY_EDITOR_AUTOSAVE, asIntegerProperty( 30 ) ),
-
- entry( KEY_R_SCRIPT, asStringProperty( "" ) ),
- entry( KEY_R_DIR, asFileProperty( USER_DIRECTORY ) ),
- entry( KEY_R_DELIM_BEGAN, asStringProperty( R_DELIM_BEGAN_DEFAULT ) ),
- entry( KEY_R_DELIM_ENDED, asStringProperty( R_DELIM_ENDED_DEFAULT ) ),
-
- entry( KEY_IMAGES_DIR, asFileProperty( USER_DIRECTORY ) ),
- entry( KEY_IMAGES_ORDER, asStringProperty( PERSIST_IMAGES_DEFAULT ) ),
- entry( KEY_IMAGES_RESIZE, asBooleanProperty( true ) ),
- entry( KEY_IMAGES_SERVER, asStringProperty( DIAGRAM_SERVER_NAME ) ),
-
- entry( KEY_DEF_PATH, asFileProperty( DEFINITION_DEFAULT ) ),
- entry( KEY_DEF_DELIM_BEGAN, asStringProperty( DEF_DELIM_BEGAN_DEFAULT ) ),
- entry( KEY_DEF_DELIM_ENDED, asStringProperty( DEF_DELIM_ENDED_DEFAULT ) ),
-
- entry( KEY_UI_RECENT_DIR, asFileProperty( USER_DIRECTORY ) ),
- entry( KEY_UI_RECENT_DOCUMENT, asFileProperty( DOCUMENT_DEFAULT ) ),
- entry( KEY_UI_RECENT_DEFINITION, asFileProperty( DEFINITION_DEFAULT ) ),
- entry( KEY_UI_RECENT_EXPORT, asFileProperty( PDF_DEFAULT ) ),
-
- //@formatter:off
- entry( KEY_UI_FONT_EDITOR_NAME, asStringProperty( FONT_NAME_EDITOR_DEFAULT ) ),
- entry( KEY_UI_FONT_EDITOR_SIZE, asDoubleProperty( FONT_SIZE_EDITOR_DEFAULT ) ),
- entry( KEY_UI_FONT_PREVIEW_NAME, asStringProperty( FONT_NAME_PREVIEW_DEFAULT ) ),
- entry( KEY_UI_FONT_PREVIEW_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_DEFAULT ) ),
- entry( KEY_UI_FONT_PREVIEW_MONO_NAME, asStringProperty( FONT_NAME_PREVIEW_MONO_NAME_DEFAULT ) ),
- entry( KEY_UI_FONT_PREVIEW_MONO_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_MONO_SIZE_DEFAULT ) ),
-
- entry( KEY_UI_WINDOW_X, asDoubleProperty( WINDOW_X_DEFAULT ) ),
- entry( KEY_UI_WINDOW_Y, asDoubleProperty( WINDOW_Y_DEFAULT ) ),
- entry( KEY_UI_WINDOW_W, asDoubleProperty( WINDOW_W_DEFAULT ) ),
- entry( KEY_UI_WINDOW_H, asDoubleProperty( WINDOW_H_DEFAULT ) ),
- entry( KEY_UI_WINDOW_MAX, asBooleanProperty() ),
- entry( KEY_UI_WINDOW_FULL, asBooleanProperty() ),
-
- entry( KEY_UI_SKIN_SELECTION, asSkinProperty( SKIN_DEFAULT ) ),
- entry( KEY_UI_SKIN_CUSTOM, asFileProperty( SKIN_CUSTOM_DEFAULT ) ),
-
- entry( KEY_UI_PREVIEW_STYLESHEET, asFileProperty( PREVIEW_CUSTOM_DEFAULT ) ),
-
- entry( KEY_LANGUAGE_LOCALE, asLocaleProperty( LOCALE_DEFAULT ) ),
-
- entry( KEY_TYPESET_CONTEXT_CLEAN, asBooleanProperty( true ) ),
- entry( KEY_TYPESET_CONTEXT_THEMES_PATH, asFileProperty( USER_DIRECTORY ) ),
- entry( KEY_TYPESET_CONTEXT_THEME_SELECTION, asStringProperty( "boschet" ) ),
- entry( KEY_TYPESET_TYPOGRAPHY_QUOTES, asBooleanProperty( true ) )
- //@formatter:on
- );
-
- private StringProperty asStringProperty( final String defaultValue ) {
- return new SimpleStringProperty( defaultValue );
- }
-
- @SuppressWarnings( "SameParameterValue" )
- private IntegerProperty asIntegerProperty( final int defaultValue ) {
- return new SimpleIntegerProperty( defaultValue );
- }
-
- private DoubleProperty asDoubleProperty( final double defaultValue ) {
- return new SimpleDoubleProperty( defaultValue );
- }
-
- private BooleanProperty asBooleanProperty() {
- return new SimpleBooleanProperty();
- }
-
- @SuppressWarnings( "SameParameterValue" )
- private BooleanProperty asBooleanProperty( final boolean defaultValue ) {
- return new SimpleBooleanProperty( defaultValue );
- }
-
- private FileProperty asFileProperty( final File defaultValue ) {
- return new FileProperty( defaultValue );
- }
-
- @SuppressWarnings( "SameParameterValue" )
- private SkinProperty asSkinProperty( final String defaultValue ) {
- return new SkinProperty( defaultValue );
- }
-
- @SuppressWarnings( "SameParameterValue" )
- private LocaleProperty asLocaleProperty( final Locale defaultValue ) {
- return new LocaleProperty( defaultValue );
- }
-
- /**
- * Helps instantiate {@link Property} instances for XML configuration items.
- */
- private static final Map<Class<?>, Function<String, Object>> UNMARSHALL =
- Map.of(
- LocaleProperty.class, LocaleProperty::parseLocale,
- SimpleBooleanProperty.class, Boolean::parseBoolean,
- SimpleIntegerProperty.class, Integer::parseInt,
- SimpleDoubleProperty.class, Double::parseDouble,
- SimpleFloatProperty.class, Float::parseFloat,
- FileProperty.class, File::new
- );
-
- private static final Map<Class<?>, Function<String, Object>> MARSHALL =
- Map.of(
- LocaleProperty.class, LocaleProperty::toLanguageTag
- );
-
- private final Map<Key, SetProperty<?>> SETS = Map.ofEntries(
- entry(
- KEY_UI_FILES_PATH,
- new SimpleSetProperty<>( observableSet( new HashSet<>() ) )
- )
- );
-
- /**
- * Creates a new {@link Workspace} that will attempt to load a configuration
- * file. If the configuration file cannot be loaded, the workspace settings
- * will return default values. This allows unit tests to provide an instance
- * of {@link Workspace} when necessary without encountering failures.
- */
- public Workspace() {
- load( FILE_PREFERENCES );
- }
-
- /**
- * Creates a new {@link Workspace} that will attempt to load the given
- * configuration file.
- *
- * @param filename The file to load.
- */
- public Workspace( final String filename ) {
- load( filename );
- }
-
- /**
- * Creates an instance of {@link ObservableList} that is based on a
- * modifiable observable array list for the given items.
- *
- * @param items The items to wrap in an observable list.
- * @param <E> The type of items to add to the list.
- * @return An observable property that can have its contents modified.
- */
- public static <E> ObservableList<E> listProperty( final Set<E> items ) {
- return new SimpleListProperty<>( observableArrayList( items ) );
- }
-
- /**
- * Returns a value that represents a setting in the application that the user
- * may configure, either directly or indirectly.
- *
- * @param key The reference to the users' preference stored in deference
- * of app reëntrance.
- * @return An observable property to be persisted.
- */
- @SuppressWarnings( "unchecked" )
- public <T, U extends Property<T>> U valuesProperty( final Key key ) {
- assert key != null;
- // The type that goes into the map must come out.
- return (U) VALUES.get( key );
- }
-
- /**
- * Returns a list of values that represent a setting in the application that
- * the user may configure, either directly or indirectly. The property
- * returned is backed by a mutable {@link Set}.
- *
- * @param key The {@link Key} associated with a preference value.
- * @return An observable property to be persisted.
- */
- @SuppressWarnings( "unchecked" )
- public <T> SetProperty<T> setsProperty( final Key key ) {
- assert key != null;
- // The type that goes into the map must come out.
- return (SetProperty<T>) SETS.get( key );
- }
-
- /**
- * Returns the {@link Boolean} preference value associated with the given
- * {@link Key}. The caller must be sure that the given {@link Key} is
- * associated with a value that matches the return type.
- *
- * @param key The {@link Key} associated with a preference value.
- * @return The value associated with the given {@link Key}.
- */
- public boolean toBoolean( final Key key ) {
- assert key != null;
- return (Boolean) valuesProperty( key ).getValue();
- }
-
- /**
- * Returns the {@link Integer} preference value associated with the given
- * {@link Key}. The caller must be sure that the given {@link Key} is
- * associated with a value that matches the return type.
- *
- * @param key The {@link Key} associated with a preference value.
- * @return The value associated with the given {@link Key}.
- */
- public int toInteger( final Key key ) {
- assert key != null;
- return (Integer) valuesProperty( key ).getValue();
- }
-
- /**
- * Returns the {@link Double} preference value associated with the given
- * {@link Key}. The caller must be sure that the given {@link Key} is
- * associated with a value that matches the return type.
- *
- * @param key The {@link Key} associated with a preference value.
- * @return The value associated with the given {@link Key}.
- */
- public double toDouble( final Key key ) {
- assert key != null;
- return (Double) valuesProperty( key ).getValue();
- }
-
- public File toFile( final Key key ) {
- assert key != null;
- return fileProperty( key ).get();
- }
-
- public String toString( final Key key ) {
- assert key != null;
- return stringProperty( key ).get();
- }
-
- private Sigils createSigils( final Key keyBegan, final Key keyEnded ) {
- assert keyBegan != null;
- assert keyEnded != null;
-
- return new Sigils( toString( keyBegan ), toString( keyEnded ) );
- }
-
- public SigilOperator createYamlSigilOperator() {
- return new YamlSigilOperator(
- createSigils( KEY_DEF_DELIM_BEGAN, KEY_DEF_DELIM_ENDED )
- );
- }
-
- public SigilOperator createRSigilOperator() {
- return new RSigilOperator(
- createSigils( KEY_R_DELIM_BEGAN, KEY_R_DELIM_ENDED ),
- createYamlSigilOperator()
- );
- }
-
- @SuppressWarnings( "SameParameterValue" )
- public IntegerProperty integerProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- @SuppressWarnings( "SameParameterValue" )
- public DoubleProperty doubleProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- /**
- * Returns the {@link File} {@link Property} associated with the given
- * {@link Key} from the internal list of preference values. The caller
- * must be sure that the given {@link Key} is associated with a {@link File}
- * {@link Property}.
- *
- * @param key The {@link Key} associated with a preference value.
- * @return The value associated with the given {@link Key}.
- */
- public ObjectProperty<File> fileProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- public ObjectProperty<String> skinProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- public LocaleProperty localeProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- /**
- * Returns the language locale setting for the
- * {@link WorkspaceKeys#KEY_LANGUAGE_LOCALE} key.
- *
- * @return The user's current locale setting.
- */
- public Locale getLocale() {
- return localeProperty( KEY_LANGUAGE_LOCALE ).toLocale();
- }
-
- public StringProperty stringProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- public BooleanProperty booleanProperty( final Key key ) {
- assert key != null;
- return valuesProperty( key );
- }
-
- public void loadValueKeys( final Consumer<Key> consumer ) {
- VALUES.keySet().forEach( consumer );
- }
-
- public void loadSetKeys( final Consumer<Key> consumer ) {
- SETS.keySet().forEach( consumer );
- }
-
- /**
- * Calls the given consumer for all single-value keys. For lists, see
- * {@link #saveSets(BiConsumer)}.
- *
- * @param consumer Called to accept each preference key value.
- */
- public void saveValues( final BiConsumer<Key, Property<?>> consumer ) {
- VALUES.forEach( consumer );
- }
-
- /**
- * Calls the given consumer for all multi-value keys. For single items, see
- * {@link #saveValues(BiConsumer)}. Callers are responsible for iterating
- * over the list of items retrieved through this method.
- *
- * @param consumer Called to accept each preference key list.
- */
- public void saveSets( final BiConsumer<Key, SetProperty<?>> consumer ) {
- SETS.forEach( consumer );
- }
-
- /**
- * Delegates to {@link #listen(Key, ReadOnlyProperty, BooleanSupplier)},
- * providing a value of {@code true} for the {@link BooleanSupplier} to
- * indicate the property changes always take effect.
- *
- * @param key The value to bind to the internal key property.
- * @param property The external property value that sets the internal value.
- */
- public <T> void listen( final Key key, final ReadOnlyProperty<T> property ) {
- listen( key, property, () -> true );
- }
-
- /**
- * Binds a read-only property to a value in the preferences. This allows
- * user interface properties to change and the preferences will be
- * synchronized automatically.
- * <p>
- * This calls {@link Platform#runLater(Runnable)} to ensure that all pending
- * application window states are finished before assessing whether property
- * changes should be applied. Without this, exiting the application while the
- * window is maximized would persist the window's maximum dimensions,
- * preventing restoration to its prior, non-maximum size.
- * </p>
- *
- * @param key The value to bind to the internal key property.
- * @param property The external property value that sets the internal value.
- * @param enabled Indicates whether property changes should be applied.
- */
- public <T> void listen(
- final Key key,
- final ReadOnlyProperty<T> property,
- final BooleanSupplier enabled ) {
- property.addListener(
- ( c, o, n ) -> runLater( () -> {
- if( enabled.getAsBoolean() ) {
- valuesProperty( key ).setValue( n );
- }
- } )
- );
- }
-
- /**
- * Saves the current workspace.
- */
- public void save() {
- try {
- final var config = new XMLConfiguration();
-
- // The root config key can only be set for an empty configuration file.
- config.setRootElementName( APP_TITLE_LOWERCASE );
- valuesProperty( KEY_META_VERSION ).setValue( getVersion() );
-
- saveValues( ( key, property ) ->
- config.setProperty( key.toString(), marshall( property ) )
- );
-
- saveSets( ( key, set ) -> {
- final var keyName = key.toString();
- set.forEach( ( value ) -> config.addProperty( keyName, value ) );
- } );
- new FileHandler( config ).save( FILE_PREFERENCES );
- } catch( final Exception ex ) {
- clue( ex );
- }
- }
-
- /**
- * Attempts to load the {@link Constants#FILE_PREFERENCES} configuration file.
- * If not found, this will fall back to an empty configuration file, leaving
- * the application to fill in default values.
- *
- * @param filename The file containing user preferences to load.
- */
- private void load( final String filename ) {
- try {
- final var config = new Configurations().xml( filename );
-
- loadValueKeys( ( key ) -> {
- final var configValue = config.getProperty( key.toString() );
-
- // Allow other properties to load, even if any are missing.
- if( configValue != null ) {
- final var propertyValue = valuesProperty( key );
- propertyValue.setValue( unmarshall( propertyValue, configValue ) );
- }
- } );
-
- loadSetKeys( ( key ) -> {
- final var configSet =
- new LinkedHashSet<>( config.getList( key.toString() ) );
- final var propertySet = setsProperty( key );
- propertySet.setValue( observableSet( configSet ) );
- } );
- } catch( final Exception ex ) {
- clue( ex );
- }
- }
-
- private Object unmarshall(
- final Property<?> property, final Object configValue ) {
- final var setting = configValue.toString();
-
- return UNMARSHALL
- .getOrDefault( property.getClass(), ( value ) -> value )
- .apply( setting );
- }
-
- private Object marshall( final Property<?> property ) {
- return property.getValue() == null
- ? null
- : MARSHALL
- .getOrDefault( property.getClass(), ( __ ) -> property.getValue() )
- .apply( property.getValue().toString() );
- }
-
- private String getYear() {
- return valueOf( Year.now().getValue() );
- }
-
+import static com.keenwrite.preferences.AppKeys.*;
+import static java.lang.String.valueOf;
+import static java.lang.System.getProperty;
+import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
+import static java.util.Map.entry;
+import static javafx.application.Platform.runLater;
+import static javafx.collections.FXCollections.observableArrayList;
+import static javafx.collections.FXCollections.observableSet;
+
+/**
+ * Responsible for defining behaviours for separate projects. A workspace has
+ * the ability to save and restore a session, including the window dimensions,
+ * tab setup, files, and user preferences.
+ * <p>
+ * The configuration must support hierarchical (nested) configuration nodes
+ * to persist the user interface state. Although possible with a flat
+ * configuration file, it's not nearly as simple or elegant.
+ * </p>
+ * <p>
+ * Neither JSON nor HOCON support schema validation and versioning, which makes
+ * XML the more suitable configuration file format. Schema validation and
+ * versioning provide future-proofing and ease of reading and upgrading previous
+ * versions of the configuration file.
+ * </p>
+ * <p>
+ * Persistent preferences may be set directly by the user or indirectly by
+ * the act of using the application.
+ * </p>
+ * <p>
+ * Note the following definitions:
+ * </p>
+ * <dl>
+ * <dt>File</dt>
+ * <dd>References a file name (no path), path, or directory.</dd>
+ * <dt>Path</dt>
+ * <dd>Fully qualified file name, which includes all parent directories.</dd>
+ * <dt>Dir</dt>
+ * <dd>Directory without file name ({@link File#isDirectory()} is true).</dd>
+ * </dl>
+ */
+public final class Workspace implements KeyConfiguration {
+ private final Map<Key, Property<?>> VALUES = Map.ofEntries(
+ entry( KEY_META_VERSION, asStringProperty( getVersion() ) ),
+ entry( KEY_META_NAME, asStringProperty( "default" ) ),
+
+ entry( KEY_DOC_TITLE, asStringProperty( "title" ) ),
+ entry( KEY_DOC_AUTHOR, asStringProperty( getProperty( "user.name" ) ) ),
+ entry( KEY_DOC_BYLINE, asStringProperty( getProperty( "user.name" ) ) ),
+ entry( KEY_DOC_ADDRESS, asStringProperty( "" ) ),
+ entry( KEY_DOC_PHONE, asStringProperty( "" ) ),
+ entry( KEY_DOC_EMAIL, asStringProperty( "" ) ),
+ entry( KEY_DOC_KEYWORDS, asStringProperty( "science, nature" ) ),
+ entry( KEY_DOC_COPYRIGHT, asStringProperty( getYear() ) ),
+ entry( KEY_DOC_DATE, asStringProperty( getDate() ) ),
+
+ entry( KEY_EDITOR_AUTOSAVE, asIntegerProperty( 30 ) ),
+
+ entry( KEY_R_SCRIPT, asStringProperty( "" ) ),
+ entry( KEY_R_DIR, asFileProperty( USER_DIRECTORY ) ),
+ entry( KEY_R_DELIM_BEGAN, asStringProperty( R_DELIM_BEGAN_DEFAULT ) ),
+ entry( KEY_R_DELIM_ENDED, asStringProperty( R_DELIM_ENDED_DEFAULT ) ),
+
+ entry( KEY_IMAGES_DIR, asFileProperty( USER_DIRECTORY ) ),
+ entry( KEY_IMAGES_ORDER, asStringProperty( PERSIST_IMAGES_DEFAULT ) ),
+ entry( KEY_IMAGES_RESIZE, asBooleanProperty( true ) ),
+ entry( KEY_IMAGES_SERVER, asStringProperty( DIAGRAM_SERVER_NAME ) ),
+
+ entry( KEY_DEF_PATH, asFileProperty( DEFINITION_DEFAULT ) ),
+ entry( KEY_DEF_DELIM_BEGAN, asStringProperty( DEF_DELIM_BEGAN_DEFAULT ) ),
+ entry( KEY_DEF_DELIM_ENDED, asStringProperty( DEF_DELIM_ENDED_DEFAULT ) ),
+
+ entry( KEY_UI_RECENT_DIR, asFileProperty( USER_DIRECTORY ) ),
+ entry( KEY_UI_RECENT_DOCUMENT, asFileProperty( DOCUMENT_DEFAULT ) ),
+ entry( KEY_UI_RECENT_DEFINITION, asFileProperty( DEFINITION_DEFAULT ) ),
+ entry( KEY_UI_RECENT_EXPORT, asFileProperty( PDF_DEFAULT ) ),
+
+ //@formatter:off
+ entry( KEY_UI_FONT_EDITOR_NAME, asStringProperty( FONT_NAME_EDITOR_DEFAULT ) ),
+ entry( KEY_UI_FONT_EDITOR_SIZE, asDoubleProperty( FONT_SIZE_EDITOR_DEFAULT ) ),
+ entry( KEY_UI_FONT_PREVIEW_NAME, asStringProperty( FONT_NAME_PREVIEW_DEFAULT ) ),
+ entry( KEY_UI_FONT_PREVIEW_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_DEFAULT ) ),
+ entry( KEY_UI_FONT_PREVIEW_MONO_NAME, asStringProperty( FONT_NAME_PREVIEW_MONO_NAME_DEFAULT ) ),
+ entry( KEY_UI_FONT_PREVIEW_MONO_SIZE, asDoubleProperty( FONT_SIZE_PREVIEW_MONO_SIZE_DEFAULT ) ),
+
+ entry( KEY_UI_WINDOW_X, asDoubleProperty( WINDOW_X_DEFAULT ) ),
+ entry( KEY_UI_WINDOW_Y, asDoubleProperty( WINDOW_Y_DEFAULT ) ),
+ entry( KEY_UI_WINDOW_W, asDoubleProperty( WINDOW_W_DEFAULT ) ),
+ entry( KEY_UI_WINDOW_H, asDoubleProperty( WINDOW_H_DEFAULT ) ),
+ entry( KEY_UI_WINDOW_MAX, asBooleanProperty() ),
+ entry( KEY_UI_WINDOW_FULL, asBooleanProperty() ),
+
+ entry( KEY_UI_SKIN_SELECTION, asSkinProperty( SKIN_DEFAULT ) ),
+ entry( KEY_UI_SKIN_CUSTOM, asFileProperty( SKIN_CUSTOM_DEFAULT ) ),
+
+ entry( KEY_UI_PREVIEW_STYLESHEET, asFileProperty( PREVIEW_CUSTOM_DEFAULT ) ),
+
+ entry( KEY_LANGUAGE_LOCALE, asLocaleProperty( LOCALE_DEFAULT ) ),
+
+ entry( KEY_TYPESET_CONTEXT_CLEAN, asBooleanProperty( true ) ),
+ entry( KEY_TYPESET_CONTEXT_THEMES_PATH, asFileProperty( USER_DIRECTORY ) ),
+ entry( KEY_TYPESET_CONTEXT_THEME_SELECTION, asStringProperty( "boschet" ) ),
+ entry( KEY_TYPESET_TYPOGRAPHY_QUOTES, asBooleanProperty( true ) )
+ //@formatter:on
+ );
+
+ /**
+ * Helps instantiate {@link Property} instances for XML configuration items.
+ */
+ private static final Map<Class<?>, Function<String, Object>> UNMARSHALL =
+ Map.of(
+ LocaleProperty.class, LocaleProperty::parseLocale,
+ SimpleBooleanProperty.class, Boolean::parseBoolean,
+ SimpleIntegerProperty.class, Integer::parseInt,
+ SimpleDoubleProperty.class, Double::parseDouble,
+ SimpleFloatProperty.class, Float::parseFloat,
+ FileProperty.class, File::new
+ );
+
+ private static final Map<Class<?>, Function<String, Object>> MARSHALL =
+ Map.of(
+ LocaleProperty.class, LocaleProperty::toLanguageTag
+ );
+
+ private final Map<Key, SetProperty<?>> SETS = Map.ofEntries(
+ entry(
+ KEY_UI_FILES_PATH,
+ new SimpleSetProperty<>( observableSet( new HashSet<>() ) )
+ )
+ );
+
+ /**
+ * Creates a new {@link Workspace} that will attempt to load a configuration
+ * file. If the configuration file cannot be loaded, the workspace settings
+ * will return default values. This allows unit tests to provide an instance
+ * of {@link Workspace} when necessary without encountering failures.
+ */
+ public Workspace() {
+ load( FILE_PREFERENCES );
+ }
+
+ /**
+ * Creates a new {@link Workspace} that will attempt to load the given
+ * configuration file.
+ *
+ * @param filename The file to load.
+ */
+ public Workspace( final String filename ) {
+ load( filename );
+ }
+
+ /**
+ * Returns a value that represents a setting in the application that the user
+ * may configure, either directly or indirectly.
+ *
+ * @param key The reference to the users' preference stored in deference
+ * of app reëntrance.
+ * @return An observable property to be persisted.
+ */
+ @SuppressWarnings( "unchecked" )
+ public <T, U extends Property<T>> U valuesProperty( final Key key ) {
+ assert key != null;
+ // The type that goes into the map must come out.
+ return (U) VALUES.get( key );
+ }
+
+ /**
+ * Creates an instance of {@link ObservableList} that is based on a
+ * modifiable observable array list for the given items.
+ *
+ * @param items The items to wrap in an observable list.
+ * @param <E> The type of items to add to the list.
+ * @return An observable property that can have its contents modified.
+ */
+ public static <E> ObservableList<E> listProperty( final Set<E> items ) {
+ return new SimpleListProperty<>( observableArrayList( items ) );
+ }
+
+ /**
+ * Returns a list of values that represent a setting in the application that
+ * the user may configure, either directly or indirectly. The property
+ * returned is backed by a mutable {@link Set}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return An observable property to be persisted.
+ */
+ @SuppressWarnings( "unchecked" )
+ public <T> SetProperty<T> setsProperty( final Key key ) {
+ assert key != null;
+ // The type that goes into the map must come out.
+ return (SetProperty<T>) SETS.get( key );
+ }
+
+ private StringProperty asStringProperty( final String defaultValue ) {
+ return new SimpleStringProperty( defaultValue );
+ }
+
+ private BooleanProperty asBooleanProperty() {
+ return new SimpleBooleanProperty();
+ }
+
+ @SuppressWarnings( "SameParameterValue" )
+ private BooleanProperty asBooleanProperty( final boolean defaultValue ) {
+ return new SimpleBooleanProperty( defaultValue );
+ }
+
+ @SuppressWarnings( "SameParameterValue" )
+ private IntegerProperty asIntegerProperty( final int defaultValue ) {
+ return new SimpleIntegerProperty( defaultValue );
+ }
+
+ private DoubleProperty asDoubleProperty( final double defaultValue ) {
+ return new SimpleDoubleProperty( defaultValue );
+ }
+
+ private FileProperty asFileProperty( final File defaultValue ) {
+ return new FileProperty( defaultValue );
+ }
+
+ @SuppressWarnings( "SameParameterValue" )
+ private LocaleProperty asLocaleProperty( final Locale defaultValue ) {
+ return new LocaleProperty( defaultValue );
+ }
+
+ @SuppressWarnings( "SameParameterValue" )
+ private SkinProperty asSkinProperty( final String defaultValue ) {
+ return new SkinProperty( defaultValue );
+ }
+
+ /**
+ * Returns the {@link String} {@link Property} associated with the given
+ * {@link Key} from the internal list of preference values. The caller
+ * must be sure that the given {@link Key} is associated with a {@link File}
+ * {@link Property}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ public StringProperty stringProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ /**
+ * Returns the {@link Boolean} {@link Property} associated with the given
+ * {@link Key} from the internal list of preference values. The caller
+ * must be sure that the given {@link Key} is associated with a {@link File}
+ * {@link Property}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ public BooleanProperty booleanProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ /**
+ * Returns the {@link Integer} {@link Property} associated with the given
+ * {@link Key} from the internal list of preference values. The caller
+ * must be sure that the given {@link Key} is associated with a {@link File}
+ * {@link Property}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ public IntegerProperty integerProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ /**
+ * Returns the {@link Double} {@link Property} associated with the given
+ * {@link Key} from the internal list of preference values. The caller
+ * must be sure that the given {@link Key} is associated with a {@link File}
+ * {@link Property}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ public DoubleProperty doubleProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ /**
+ * Returns the {@link File} {@link Property} associated with the given
+ * {@link Key} from the internal list of preference values. The caller
+ * must be sure that the given {@link Key} is associated with a {@link File}
+ * {@link Property}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ public ObjectProperty<File> fileProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ /**
+ * Returns the {@link Locale} {@link Property} associated with the given
+ * {@link Key} from the internal list of preference values. The caller
+ * must be sure that the given {@link Key} is associated with a {@link File}
+ * {@link Property}.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ public LocaleProperty localeProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ public ObjectProperty<String> skinProperty( final Key key ) {
+ assert key != null;
+ return valuesProperty( key );
+ }
+
+ @Override
+ public String getString( final Key key ) {
+ assert key != null;
+ return stringProperty( key ).get();
+ }
+
+ /**
+ * Returns the {@link Boolean} preference value associated with the given
+ * {@link Key}. The caller must be sure that the given {@link Key} is
+ * associated with a value that matches the return type.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ @Override
+ public boolean getBoolean( final Key key ) {
+ assert key != null;
+ return booleanProperty( key ).get();
+ }
+
+ /**
+ * Returns the {@link Integer} preference value associated with the given
+ * {@link Key}. The caller must be sure that the given {@link Key} is
+ * associated with a value that matches the return type.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ @Override
+ public int getInteger( final Key key ) {
+ assert key != null;
+ return integerProperty( key ).get();
+ }
+
+ /**
+ * Returns the {@link Double} preference value associated with the given
+ * {@link Key}. The caller must be sure that the given {@link Key} is
+ * associated with a value that matches the return type.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ @Override
+ public double getDouble( final Key key ) {
+ assert key != null;
+ return doubleProperty( key ).get();
+ }
+
+ /**
+ * Returns the {@link File} preference value associated with the given
+ * {@link Key}. The caller must be sure that the given {@link Key} is
+ * associated with a value that matches the return type.
+ *
+ * @param key The {@link Key} associated with a preference value.
+ * @return The value associated with the given {@link Key}.
+ */
+ @Override
+ public File asFile( final Key key ) {
+ assert key != null;
+ return fileProperty( key ).get();
+ }
+
+ /**
+ * Returns the language locale setting for the
+ * {@link AppKeys#KEY_LANGUAGE_LOCALE} key.
+ *
+ * @return The user's current locale setting.
+ */
+ public Locale getLocale() {
+ return localeProperty( KEY_LANGUAGE_LOCALE ).toLocale();
+ }
+
+ private Sigils createSigils( final Key keyBegan, final Key keyEnded ) {
+ assert keyBegan != null;
+ assert keyEnded != null;
+
+ return new Sigils( getString( keyBegan ), getString( keyEnded ) );
+ }
+
+ public SigilOperator createYamlSigilOperator() {
+ return new YamlSigilOperator(
+ createSigils( KEY_DEF_DELIM_BEGAN, KEY_DEF_DELIM_ENDED )
+ );
+ }
+
+ public SigilOperator createRSigilOperator() {
+ return new RSigilOperator(
+ createSigils( KEY_R_DELIM_BEGAN, KEY_R_DELIM_ENDED ),
+ createYamlSigilOperator()
+ );
+ }
+
+ /**
+ * Delegates to {@link #listen(Key, ReadOnlyProperty, BooleanSupplier)},
+ * providing a value of {@code true} for the {@link BooleanSupplier} to
+ * indicate the property changes always take effect.
+ *
+ * @param key The value to bind to the internal key property.
+ * @param property The external property value that sets the internal value.
+ */
+ public <T> void listen( final Key key, final ReadOnlyProperty<T> property ) {
+ listen( key, property, () -> true );
+ }
+
+ /**
+ * Binds a read-only property to a value in the preferences. This allows
+ * user interface properties to change and the preferences will be
+ * synchronized automatically.
+ * <p>
+ * This calls {@link Platform#runLater(Runnable)} to ensure that all pending
+ * application window states are finished before assessing whether property
+ * changes should be applied. Without this, exiting the application while the
+ * window is maximized would persist the window's maximum dimensions,
+ * preventing restoration to its prior, non-maximum size.
+ * </p>
+ *
+ * @param key The value to bind to the internal key property.
+ * @param property The external property value that sets the internal value.
+ * @param enabled Indicates whether property changes should be applied.
+ */
+ public <T> void listen(
+ final Key key,
+ final ReadOnlyProperty<T> property,
+ final BooleanSupplier enabled ) {
+ property.addListener(
+ ( c, o, n ) -> runLater( () -> {
+ if( enabled.getAsBoolean() ) {
+ valuesProperty( key ).setValue( n );
+ }
+ } )
+ );
+ }
+
+ public void loadValueKeys( final Consumer<Key> consumer ) {
+ VALUES.keySet().forEach( consumer );
+ }
+
+ public void loadSetKeys( final Consumer<Key> consumer ) {
+ SETS.keySet().forEach( consumer );
+ }
+
+ /**
+ * Calls the given consumer for all single-value keys. For lists, see
+ * {@link #saveSets(BiConsumer)}.
+ *
+ * @param consumer Called to accept each preference key value.
+ */
+ public void saveValues( final BiConsumer<Key, Property<?>> consumer ) {
+ VALUES.forEach( consumer );
+ }
+
+ /**
+ * Calls the given consumer for all multi-value keys. For single items, see
+ * {@link #saveValues(BiConsumer)}. Callers are responsible for iterating
+ * over the list of items retrieved through this method.
+ *
+ * @param consumer Called to accept each preference key list.
+ */
+ public void saveSets( final BiConsumer<Key, SetProperty<?>> consumer ) {
+ SETS.forEach( consumer );
+ }
+
+ /**
+ * Saves the current workspace.
+ */
+ public void save() {
+ try {
+ final var config = new XMLConfiguration();
+
+ // The root config key can only be set for an empty configuration file.
+ config.setRootElementName( APP_TITLE_LOWERCASE );
+ valuesProperty( KEY_META_VERSION ).setValue( getVersion() );
+
+ saveValues( ( key, property ) ->
+ config.setProperty( key.toString(), marshall( property ) )
+ );
+
+ saveSets( ( key, set ) -> {
+ final var keyName = key.toString();
+ set.forEach( ( value ) -> config.addProperty( keyName, value ) );
+ } );
+ new FileHandler( config ).save( FILE_PREFERENCES );
+ } catch( final Exception ex ) {
+ clue( ex );
+ }
+ }
+
+ /**
+ * Attempts to load the {@link Constants#FILE_PREFERENCES} configuration file.
+ * If not found, this will fall back to an empty configuration file, leaving
+ * the application to fill in default values.
+ *
+ * @param filename The file containing user preferences to load.
+ */
+ private void load( final String filename ) {
+ try {
+ final var config = new Configurations().xml( filename );
+
+ loadValueKeys( ( key ) -> {
+ final var configValue = config.getProperty( key.toString() );
+
+ // Allow other properties to load, even if any are missing.
+ if( configValue != null ) {
+ final var propertyValue = valuesProperty( key );
+ propertyValue.setValue( unmarshall( propertyValue, configValue ) );
+ }
+ } );
+
+ loadSetKeys( ( key ) -> {
+ final var configSet =
+ new LinkedHashSet<>( config.getList( key.toString() ) );
+ final var propertySet = setsProperty( key );
+ propertySet.setValue( observableSet( configSet ) );
+ } );
+ } catch( final Exception ex ) {
+ clue( ex );
+ }
+ }
+
+ private Object unmarshall(
+ final Property<?> property, final Object configValue ) {
+ final var setting = configValue.toString();
+
+ return UNMARSHALL
+ .getOrDefault( property.getClass(), ( value ) -> value )
+ .apply( setting );
+ }
+
+ private Object marshall( final Property<?> property ) {
+ return property.getValue() == null
+ ? null
+ : MARSHALL
+ .getOrDefault( property.getClass(), ( __ ) -> property.getValue() )
+ .apply( property.getValue().toString() );
+ }
+
+ /**
+ * Returns the current year. This is used to populate the default year
+ * (e.g., for copyright notices).
+ *
+ * @return The current year.
+ */
+ private String getYear() {
+ return valueOf( Year.now().getValue() );
+ }
+
+ /**
+ * Returns the current date. This is used to populate the default date
+ * (e.g., for document publication date).
+ *
+ * @return The current year.
+ */
private String getDate() {
return ZonedDateTime.now().format( RFC_1123_DATE_TIME );
src/main/java/com/keenwrite/preferences/WorkspaceKeys.java
-/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
-package com.keenwrite.preferences;
-
-import static com.keenwrite.preferences.Key.key;
-
-/**
- * Responsible for defining constants used throughout the application that
- * represent persisted preferences.
- */
-public final class WorkspaceKeys {
- //@formatter:off
- private static final Key KEY_ROOT = key( "workspace" );
-
- public static final Key KEY_META = key( KEY_ROOT, "meta" );
- public static final Key KEY_META_NAME = key( KEY_META, "name" );
- public static final Key KEY_META_VERSION = key( KEY_META, "version" );
-
- public static final Key KEY_DOC = key( KEY_ROOT, "document" );
- public static final Key KEY_DOC_TITLE = key( KEY_DOC, "title" );
- public static final Key KEY_DOC_AUTHOR = key( KEY_DOC, "author" );
- public static final Key KEY_DOC_BYLINE = key( KEY_DOC, "byline" );
- public static final Key KEY_DOC_ADDRESS = key( KEY_DOC, "address" );
- public static final Key KEY_DOC_PHONE = key( KEY_DOC, "phone" );
- public static final Key KEY_DOC_EMAIL = key( KEY_DOC, "email" );
- public static final Key KEY_DOC_KEYWORDS = key( KEY_DOC, "keywords" );
- public static final Key KEY_DOC_DATE = key( KEY_DOC, "date" );
- public static final Key KEY_DOC_COPYRIGHT = key( KEY_DOC, "copyright" );
-
- public static final Key KEY_EDITOR = key( KEY_ROOT, "editor" );
- public static final Key KEY_EDITOR_AUTOSAVE = key( KEY_EDITOR, "autosave" );
-
- public static final Key KEY_R = key( KEY_ROOT, "r" );
- public static final Key KEY_R_SCRIPT = key( KEY_R, "script" );
- public static final Key KEY_R_DIR = key( KEY_R, "dir" );
- public static final Key KEY_R_DELIM = key( KEY_R, "delimiter" );
- public static final Key KEY_R_DELIM_BEGAN = key( KEY_R_DELIM, "began" );
- public static final Key KEY_R_DELIM_ENDED = key( KEY_R_DELIM, "ended" );
-
- public static final Key KEY_IMAGES = key( KEY_ROOT, "images" );
- public static final Key KEY_IMAGES_DIR = key( KEY_IMAGES, "dir" );
- public static final Key KEY_IMAGES_ORDER = key( KEY_IMAGES, "order" );
- public static final Key KEY_IMAGES_RESIZE = key( KEY_IMAGES, "resize" );
- public static final Key KEY_IMAGES_SERVER = key( KEY_IMAGES, "server" );
-
- public static final Key KEY_DEF = key( KEY_ROOT, "definition" );
- public static final Key KEY_DEF_PATH = key( KEY_DEF, "path" );
- public static final Key KEY_DEF_DELIM = key( KEY_DEF, "delimiter" );
- public static final Key KEY_DEF_DELIM_BEGAN = key( KEY_DEF_DELIM, "began" );
- public static final Key KEY_DEF_DELIM_ENDED = key( KEY_DEF_DELIM, "ended" );
-
- public static final Key KEY_UI = key( KEY_ROOT, "ui" );
-
- public static final Key KEY_UI_RECENT = key( KEY_UI, "recent" );
- public static final Key KEY_UI_RECENT_DIR = key( KEY_UI_RECENT, "dir" );
- public static final Key KEY_UI_RECENT_DOCUMENT = key( KEY_UI_RECENT, "document" );
- public static final Key KEY_UI_RECENT_DEFINITION = key( KEY_UI_RECENT, "definition" );
- public static final Key KEY_UI_RECENT_EXPORT = key( KEY_UI_RECENT, "export" );
-
- public static final Key KEY_UI_FILES = key( KEY_UI, "files" );
- public static final Key KEY_UI_FILES_PATH = key( KEY_UI_FILES, "path" );
-
- public static final Key KEY_UI_FONT = key( KEY_UI, "font" );
- public static final Key KEY_UI_FONT_EDITOR = key( KEY_UI_FONT, "editor" );
- public static final Key KEY_UI_FONT_EDITOR_NAME = key( KEY_UI_FONT_EDITOR, "name" );
- public static final Key KEY_UI_FONT_EDITOR_SIZE = key( KEY_UI_FONT_EDITOR, "size" );
- public static final Key KEY_UI_FONT_PREVIEW = key( KEY_UI_FONT, "preview" );
- public static final Key KEY_UI_FONT_PREVIEW_NAME = key( KEY_UI_FONT_PREVIEW, "name" );
- public static final Key KEY_UI_FONT_PREVIEW_SIZE = key( KEY_UI_FONT_PREVIEW, "size" );
- public static final Key KEY_UI_FONT_PREVIEW_MONO = key( KEY_UI_FONT_PREVIEW, "mono" );
- public static final Key KEY_UI_FONT_PREVIEW_MONO_NAME = key( KEY_UI_FONT_PREVIEW_MONO, "name" );
- public static final Key KEY_UI_FONT_PREVIEW_MONO_SIZE = key( KEY_UI_FONT_PREVIEW_MONO, "size" );
-
- public static final Key KEY_UI_WINDOW = key( KEY_UI, "window" );
- public static final Key KEY_UI_WINDOW_X = key( KEY_UI_WINDOW, "x" );
- public static final Key KEY_UI_WINDOW_Y = key( KEY_UI_WINDOW, "y" );
- public static final Key KEY_UI_WINDOW_W = key( KEY_UI_WINDOW, "width" );
- public static final Key KEY_UI_WINDOW_H = key( KEY_UI_WINDOW, "height" );
- public static final Key KEY_UI_WINDOW_MAX = key( KEY_UI_WINDOW, "maximized" );
- public static final Key KEY_UI_WINDOW_FULL = key( KEY_UI_WINDOW, "full" );
-
- public static final Key KEY_UI_SKIN = key( KEY_UI, "skin" );
- public static final Key KEY_UI_SKIN_SELECTION = key( KEY_UI_SKIN, "selection" );
- public static final Key KEY_UI_SKIN_CUSTOM = key( KEY_UI_SKIN, "custom" );
-
- public static final Key KEY_UI_PREVIEW = key( KEY_UI, "preview" );
- public static final Key KEY_UI_PREVIEW_STYLESHEET = key( KEY_UI_PREVIEW, "stylesheet" );
-
- public static final Key KEY_LANGUAGE = key( KEY_ROOT, "language" );
- public static final Key KEY_LANGUAGE_LOCALE = key( KEY_LANGUAGE, "locale" );
-
- public static final Key KEY_TYPESET = key( KEY_ROOT, "typeset" );
- public static final Key KEY_TYPESET_CONTEXT = key( KEY_TYPESET, "context" );
- public static final Key KEY_TYPESET_CONTEXT_THEMES = key( KEY_TYPESET_CONTEXT, "themes" );
- public static final Key KEY_TYPESET_CONTEXT_THEMES_PATH = key( KEY_TYPESET_CONTEXT_THEMES, "path" );
- public static final Key KEY_TYPESET_CONTEXT_THEME_SELECTION = key( KEY_TYPESET_CONTEXT_THEMES, "selection" );
- public static final Key KEY_TYPESET_CONTEXT_CLEAN = key( KEY_TYPESET_CONTEXT, "clean" );
- public static final Key KEY_TYPESET_TYPOGRAPHY = key( KEY_TYPESET, "typography" );
- public static final Key KEY_TYPESET_TYPOGRAPHY_QUOTES = key( KEY_TYPESET_TYPOGRAPHY, "quotes" );
- //@formatter:on
-
- /**
- * Only for constants, do not instantiate.
- */
- private WorkspaceKeys() { }
-}