| 2 | 2 | <project version="4"> |
| 3 | 3 | <component name="CompilerConfiguration"> |
| 4 | <bytecodeTargetLevel target="11" /> | |
| 4 | <bytecodeTargetLevel target="14" /> | |
| 5 | 5 | </component> |
| 6 | 6 | </project> |
| 2 | 2 | <project version="4"> |
| 3 | 3 | <component name="ExternalStorageConfigurationManager" enabled="true" /> |
| 4 | <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="14" project-jdk-type="JavaSDK"> | |
| 4 | <component name="ProjectRootManager" version="2" languageLevel="JDK_14" default="true" project-jdk-name="14" project-jdk-type="JavaSDK"> | |
| 5 | 5 | <output url="file://$PROJECT_DIR$/build" /> |
| 6 | 6 | </component> |
| 202 | 202 | # Returns a human-readable string that provides the elapsed time between |
| 203 | 203 | # two numbers in terms of years, months, and days. If any unit value is zero, |
| 204 | # the unit is not included. The words (year, month, day) are pluralised | |
| 204 | # the unit is not included. The words (year, month, day) are pluralized | |
| 205 | 205 | # according to English grammar. The numbers are written out according to |
| 206 | 206 | # Chicago Manual of Style. This applies the serial comma. |
| ... | ||
| 288 | 288 | # ----------------------------------------------------------------------------- |
| 289 | 289 | pl.numeric <- function( s, n ) { |
| 290 | concat( cms( n ), concat( " ", pluralise( s, n ) ) ) | |
| 290 | concat( cms( n ), concat( " ", pluralize( s, n ) ) ) | |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | 293 | # ----------------------------------------------------------------------------- |
| 294 | # Pluralise s if n is not equal to 1. | |
| 294 | # Pluralize s if n is not equal to 1. | |
| 295 | 295 | # ----------------------------------------------------------------------------- |
| 296 | 296 | pl <- function( s, n=2 ) { |
| 33 | 33 | // JavaFX |
| 34 | 34 | implementation 'org.reactfx:reactfx:1.4.1' |
| 35 | implementation 'org.controlsfx:controlsfx:11.0.1' | |
| 35 | implementation 'org.controlsfx:controlsfx:11.0.2' | |
| 36 | 36 | implementation 'org.fxmisc.richtext:richtextfx:0.10.5' |
| 37 | 37 | implementation 'org.fxmisc.wellbehaved:wellbehavedfx:0.3.3' |
| ... | ||
| 132 | 132 | |
| 133 | 133 | version = gitVersion() |
| 134 | sourceCompatibility = JavaVersion.VERSION_11 | |
| 135 | 134 | |
| 136 | 135 | def launcherClassName = "com.${applicationName}.Launcher" |
| 37 | 37 | rm( app_home + "/variables.yaml" ) |
| 38 | 38 | rm( app_home + "/untitled.md" ) |
| 39 | rm( home + "/.scrivenvar" ) | |
| 39 | rm( dir_home + "/.scrivenvar" ) | |
| 40 | 40 | |
| 41 | 41 | # --------------------------------------------------------------- |
| ... | ||
| 50 | 50 | set_typing_speed( 240 ) |
| 51 | 51 | |
| 52 | header( "What is this application?" ) | |
| 52 | heading( "What is this application?" ) | |
| 53 | 53 | typer( "Well, this application is a text editor that supports interpolated definitions, ") |
| 54 | 54 | typer( "a few different text formats, real-time preview, spell check ") |
| ... | ||
| 63 | 63 | # Definition demo |
| 64 | 64 | # --------------------------------------------------------------- |
| 65 | header( "What are definitions?" ) | |
| 65 | heading( "What are definitions?" ) | |
| 66 | 66 | typer( "Watch. " ) |
| 67 | 67 | wait( .5 ) |
| ... | ||
| 102 | 102 | paragraph() |
| 103 | 103 | |
| 104 | header( "What is interpolation?" ) | |
| 104 | heading( "What is interpolation?" ) | |
| 105 | 105 | typer( "Definition values can reference definition names. " ) |
| 106 | 106 | wait( .5 ) |
| 107 | 107 | typer( "The definition names act as placeholders. Substituting placeholders with " ) |
| 108 | typer( "their defined value is called *interpolation*. Let's see how it works." ) | |
| 108 | typer( "their definition value is called *interpolation*. Let's see how it works." ) | |
| 109 | 109 | wait( 2 ) |
| 110 | 110 | |
| 33 | 33 | |
| 34 | 34 | set_typing_speed( 80 ) |
| 35 | ||
| 35 | 36 | file_open() |
| 36 | 37 | type( Key.UP, Key.ALT ) |
| ... | ||
| 48 | 49 | |
| 49 | 50 | paragraph() |
| 50 | header( "What text formats are supported?" ) | |
| 51 | heading( "What text formats are supported?" ) | |
| 51 | 52 | |
| 52 | 53 | typer( "Scr" ) |
| 53 | 54 | autoinsert() |
| 54 | 55 | typer( " supports Markdown, R Markdown, XML, and R XML; however, the software " ) |
| 55 | 56 | typer( "architecture enables it to easily add new formats. The following figure " ) |
| 56 | 57 | typer( "depicts the overall architecture: " ) |
| 57 | 58 | paragraph() |
| 58 | 59 | typer( "" ) |
| 59 | 60 | paragraph() |
| 60 | typer( "Most text editors read a single format and convert it to one other format. " ) | |
| 61 | typer( "With a little more effort, text editors can support many input and output " ) | |
| 62 | typer( "formats. Scr" ) | |
| 61 | typer( "Many text editors can only open one type of plain text markup format that is " ) | |
| 62 | typer( "only output as HTML. With a little more effort, text editors could support " ) | |
| 63 | typer( "multiple input and output formats. Scr" ) | |
| 63 | 64 | autoinsert() |
| 64 | typer( " goes one step further by introducing interpolated definitions." ) | |
| 65 | typer( " does so and goes one step further by introducing interpolated definitions." ) | |
| 65 | 66 | paragraph() |
| 66 | 67 | typer( "Kitten interlude:" ) |
| 67 | 68 | paragraph() |
| 68 | 69 | typer( "" ) |
| 69 | 70 | paragraph() |
| 70 | 71 | |
| 71 | header( "What is R?" ) | |
| 72 | heading( "What is R?" ) | |
| 72 | 73 | typer( "R is a programming language. You might have noticed a few potential grammar " ) |
| 73 | 74 | typer( "problems with direct substitution. Rules for possessive forms, numbers, and " ) |
| 74 | typer( "other linguistic exceptions can be addressed using R. Let's take a look!" ) | |
| 75 | typer( "other quirks can be tackled using R." ) | |
| 76 | ||
| 77 | # ----------------------------------------------------------------------------- | |
| 78 | # Demo bootstrapping | |
| 79 | # ----------------------------------------------------------------------------- | |
| 80 | ||
| 81 | # Jump to the end | |
| 82 | type( Key.END, Key.CTRL ) | |
| 83 | paragraph() | |
| 84 | ||
| 85 | set_typing_speed( 300 ) | |
| 86 | heading( "How is R used?" ) | |
| 87 | typer( "R must be instructed where to find script files and what ones to load. The " ) | |
| 88 | typer( "*working directory* is the full path to those R files; the *startup script* " ) | |
| 89 | typer( "defines what R files to load. Both preferences must be changed before prose " ) | |
| 90 | typer( "may be processed. Preferences can be opened using either the " ) | |
| 91 | typeln( "**Edit > Preferences** menu or by pressing `Ctrl+Alt+s`. Here goes!" ) | |
| 92 | wait( 5 ) | |
| 93 | ||
| 94 | # ----------------------------------------------------------------------------- | |
| 95 | # Select the R script directory | |
| 96 | # ----------------------------------------------------------------------------- | |
| 97 | ||
| 98 | # Change the working directory by clicking "Browse" | |
| 99 | type( "s", Key.CTRL + Key.ALT ) | |
| 100 | wait("1594592396134.png", 1) | |
| 101 | click("1594592396134.png") | |
| 102 | wait( 0.5 ) | |
| 103 | ||
| 104 | # Navigate to and select the "r" directory | |
| 105 | type( Key.UP, Key.ALT ) | |
| 106 | wait( 0.5 ) | |
| 107 | end() | |
| 108 | wait( 0.5 ) | |
| 109 | enter() | |
| 110 | wait( 0.5 ) | |
| 111 | end() | |
| 112 | wait( 0.5 ) | |
| 113 | type( Key.UP ) | |
| 114 | wait( 0.5 ) | |
| 115 | recur( 2, tab ) | |
| 116 | wait( 0.5 ) | |
| 117 | enter() | |
| 118 | wait( 1 ) | |
| 119 | ||
| 120 | # ----------------------------------------------------------------------------- | |
| 121 | # Set the R startup script instructions | |
| 122 | # ----------------------------------------------------------------------------- | |
| 123 | ||
| 124 | wait("1594593710440.png", 5) | |
| 125 | click("1594593710440.png") | |
| 126 | ||
| 127 | set_typing_speed( 440 ) | |
| 128 | ||
| 129 | typeln( "setwd( '$application.r.working.directory$' )" ) | |
| 130 | typeln( "assign( ""anchor"", '$date.anchor$', envir = .GlobalEnv )" ) | |
| 131 | typeln( "source( 'pluralize.R' )" ) | |
| 132 | typeln( "source( 'possessive.R' )" ) | |
| 133 | typeln( "source( 'conversion.R' )" ) | |
| 134 | typeln( "source( 'csv.R' )" ) | |
| 135 | wait( 2 ) | |
| 136 | ||
| 137 | wait("1594593794335.png", 5) | |
| 138 | click("1594593794335.png") | |
| 139 | ||
| 140 | paragraph() | |
| 141 | set_typing_speed( 220 ) | |
| 142 | ||
| 143 | typer( "R is now configured. The startup script and other R " ) | |
| 144 | typer( "files can be found in the " ) | |
| 145 | typer( "[repository](https://github.com/DaveJarvis/scrivenvar/tree/master/R). " ) | |
| 146 | wait( 2.25 ) | |
| 147 | ||
| 148 | wait("1594594984108.png", 5) | |
| 149 | click("1594594984108.png") | |
| 150 | ||
| 151 | wait( 5 ) | |
| 152 | click("1594595370191.png") | |
| 153 | ||
| 154 | paragraph() | |
| 155 | typer( "Next, we'll see how definitions and R can work together." ) | |
| 156 | wait( 2 ) | |
| 75 | 157 | |
| 32 | 32 | from editor import * |
| 33 | 33 | |
| 34 | set_typing_speed( 220 ) | |
| 35 | ||
| 34 | 36 | # ----------------------------------------------------------------------------- |
| 35 | 37 | # Open the demo text. |
| ... | ||
| 48 | 50 | |
| 49 | 51 | # ----------------------------------------------------------------------------- |
| 50 | # Demo bootstrapping | |
| 52 | # Re-open the corresponding definition file. | |
| 51 | 53 | # ----------------------------------------------------------------------------- |
| 54 | file_open() | |
| 55 | recur( 2, down ) | |
| 56 | wait( 1 ) | |
| 57 | enter() | |
| 58 | wait( 1 ) | |
| 52 | 59 | |
| 53 | # Jump to the end. | |
| 54 | type( Key.END, Key.CTRL ) | |
| 60 | # ----------------------------------------------------------------------------- | |
| 61 | # Brief introduction to R | |
| 62 | # ----------------------------------------------------------------------------- | |
| 63 | type( Key.HOME, Key.CTRL ) | |
| 64 | end() | |
| 55 | 65 | paragraph() |
| 56 | ||
| 57 | type( "s", Key.CTRL + Key.ALT ) | |
| 58 | wait( 0.25 ) | |
| 59 | 66 | |
| 67 | typer( "## Using R" ) | |
| 68 | paragraph() | |
| 69 | typer( "Insert R code into documents as follows: `r# 1+1`. " ) | |
| 70 | wait( 1.5 ) | |
| 71 | typer( "Notice how the right-hand pane shows the computed result. I'll wait. " ) | |
| 72 | wait( 3 ) | |
| 73 | typer( "The syntax is: open backtick, r#, *computable expression*, close " ) | |
| 74 | typer( "backtick. That expression can be any valid R statement. The status bar " ) | |
| 75 | typer( "will provide clues when an R expression cannot be computed by the " ) | |
| 76 | typer( "editor. `r# glitch`" ) | |
| 77 | wait( 4 ) | |
| 78 | recur( 11, backspace ) | |
| 79 | typer( "Let's swap 34 storeys for a definition value and replace the number " ) | |
| 80 | typer( "according to the Chicago Manual of Style (cms) rules." ) | |
| 60 | 81 | |
| 61 | 82 | # ----------------------------------------------------------------------------- |
| 62 | 83 | # Demo pluralization |
| 63 | 84 | # ----------------------------------------------------------------------------- |
| 85 | set_typing_speed( 80 ) | |
| 86 | ||
| 87 | edit_find( "34" ) | |
| 88 | autoinsert() | |
| 89 | ||
| 90 | edit_find( "x(" ) | |
| 91 | typer( "cms(" ) | |
| 92 | ||
| 93 | edit_find( "storeys." ) | |
| 94 | typer( "34." ) | |
| 95 | autoinsert() | |
| 96 | edit_find( "x(" ) | |
| 97 | typer( "pl( 'storey'," ) | |
| 98 | wait( 4 ) | |
| 64 | 99 | |
| 100 | tab() | |
| 101 | rename_definition( "1" ) | |
| 102 | wait( 4 ) | |
| 103 | rename_definition( "142" ) | |
| 104 | wait( 4 ) | |
| 105 | rename_definition( "34" ) | |
| 106 | wait( 4 ) | |
| 107 | tab() | |
| 65 | 108 | |
| 66 | 109 | # ----------------------------------------------------------------------------- |
| 67 | # Demo possessives (it, she, he, Ross) | |
| 110 | # Demo possessives (it, her, his, Director) | |
| 68 | 111 | # ----------------------------------------------------------------------------- |
| 112 | type( Key.HOME, Key.CTRL ) | |
| 113 | edit_find( "Director" ) | |
| 114 | autoinsert() | |
| 115 | edit_find_next() | |
| 116 | autoinsert() | |
| 117 | edit_find_next() | |
| 118 | autoinsert() | |
| 119 | type( Key.RIGHT ) | |
| 120 | recur( 2, delete ) | |
| 121 | autoinsert() | |
| 122 | home() | |
| 123 | edit_find( "x(" ) | |
| 124 | typer( "pos(" ) | |
| 125 | wait( 2 ) | |
| 126 | ||
| 127 | tab() | |
| 128 | rename_definition( "Headmistress" ) | |
| 129 | wait( 4 ) | |
| 130 | rename_definition( "Director" ) | |
| 131 | wait( 2 ) | |
| 132 | tab() | |
| 69 | 133 | |
| 134 | type( Key.END, Key.CTRL ) | |
| 135 | paragraph() | |
| 136 | typer( "Other possessives: `r# pos( 'it' )`, `r# pos( 'her' )`, `r# pos( 'his' )`, " ) | |
| 137 | typer( "and `r# pos( 'my' )`." ) | |
| 70 | 138 | |
| 71 | 139 | # ----------------------------------------------------------------------------- |
| 72 | 140 | # Demo conversion, including ordinal numbers |
| 73 | 141 | # ----------------------------------------------------------------------------- |
| 142 | set_typing_speed( 160 ) | |
| 74 | 143 | |
| 75 | # ----------------------------------------------------------------------------- | |
| 76 | # Demo Chicago Manual of Style | |
| 77 | # ----------------------------------------------------------------------------- | |
| 144 | paragraph() | |
| 145 | heading( "Date Conversions" ) | |
| 146 | typer( "Mixing R code with definitions invites endless possibilities. " ) | |
| 147 | typer( "Imagine someone racing to the " ) | |
| 148 | typer( "`r#cms( v$location$breeder$storeys, ordinal=TRUE )` floor, whereby that " ) | |
| 149 | typer( "ordinal stems from the Hatchery's storeys' definition. Or how about " ) | |
| 150 | typer( "a complex timeline where dates are expressed in days relative to one " ) | |
| 151 | typer( "point in time. Let's call this the *anchor date* and define it." ) | |
| 152 | ||
| 153 | tab() | |
| 154 | home() | |
| 155 | typer( Key.SPACE ) | |
| 156 | insert() | |
| 157 | rename_definition( "date" ) | |
| 158 | insert() | |
| 159 | rename_definition( "anchor" ) | |
| 160 | insert() | |
| 161 | rename_definition( "1969-10-29" ) | |
| 162 | tab() | |
| 163 | ||
| 164 | paragraph() | |
| 165 | typer( "Next, set an R variable named `now` to the current date" ) | |
| 166 | typer( "`r# now = format( Sys.time(), '%Y-%m-%d' ); ''`--- the empty single quotes " ) | |
| 167 | typer( "prevent the date from appearing in the output document. " ) | |
| 78 | 168 | |
| 169 | paragraph() | |
| 170 | typer( "We set the anchor date to `r# annal()`, which was " ) | |
| 171 | typer( "`r# elapsed( 0, days( v$date$anchor, format( Sys.time(), '%Y-%m-%d' ) ) )` " ) | |
| 172 | typer( "ago from `r# format( as.Date( now ), '%B %d, %Y' )`. " ) | |
| 79 | 173 | |
| 80 | 174 | # ----------------------------------------------------------------------------- |
| 81 | 175 | # Demo CSV file import |
| 176 | # ----------------------------------------------------------------------------- | |
| 177 | paragraph() | |
| 178 | heading( "Tabular Data" ) | |
| 179 | typer( "The following table shows average Canadian lifespans by birth " ) | |
| 180 | typer( "year and sex:" ) | |
| 181 | paragraph() | |
| 182 | typer( "`r# csv2md( '../data.csv', total=FALSE )`" ) | |
| 183 | paragraph() | |
| 184 | typer( "Calling `csv2md` converts the comma-separated values in the spreadsheet " ) | |
| 185 | typer( "to a table formatted using Markdown. The HTML preview pane changes the " ) | |
| 186 | typer( "appearance of the resulting table. Using `../data.csv` instructs R to " ) | |
| 187 | typer( "open `data.csv` from one directory above the *working directory*." ) | |
| 188 | ||
| 189 | # ----------------------------------------------------------------------------- | |
| 190 | # Demo HTML export | |
| 82 | 191 | # ----------------------------------------------------------------------------- |
| 192 | paragraph() | |
| 193 | heading( "Export" ) | |
| 194 | typer( "Retrieve the output HTML by using the **Edit > Copy HTML** menu. Let's " ) | |
| 195 | typer( "look at the output document." ) | |
| 83 | 196 | |
| 197 | type( "e", Key.ALT ) | |
| 198 | wait( 0.5 ) | |
| 199 | down() | |
| 200 | wait( 0.25 ) | |
| 201 | enter() | |
| 202 | wait( 0.25 ) | |
| 84 | 203 | |
| 204 | type( "a", Key.CTRL ) | |
| 205 | wait( 0.25 ) | |
| 206 | type( "v", Key.CTRL ) | |
| 85 | 207 | |
| 33 | 33 | from os.path import expanduser |
| 34 | 34 | |
| 35 | home = expanduser( "~" ) | |
| 36 | app_home = home + "/bin" | |
| 35 | dir_home = expanduser( "~" ) | |
| 36 | app_home = dir_home + "/bin" | |
| 37 | 37 | app_bin = app_home + "/scrivenvar.jar" |
| 38 | 38 | |
| ... | ||
| 92 | 92 | type( c ) |
| 93 | 93 | random_wait() |
| 94 | ||
| 95 | # ----------------------------------------------------------------------------- | |
| 96 | # Type a line of text followed by typing the ENTER key. | |
| 97 | # ----------------------------------------------------------------------------- | |
| 98 | def typeln( text ): | |
| 99 | typer( text ) | |
| 100 | enter() | |
| 94 | 101 | |
| 95 | 102 | # ----------------------------------------------------------------------------- |
| ... | ||
| 175 | 182 | # Writes a heading to the document using the given text value as the content. |
| 176 | 183 | # ----------------------------------------------------------------------------- |
| 177 | def header( text ): | |
| 184 | def heading( text ): | |
| 178 | 185 | typer( "# " + text ) |
| 179 | 186 | paragraph() |
| 149 | 149 | exportDefinitions( getDefinitionPath() ); |
| 150 | 150 | interpolateResolvedMap(); |
| 151 | resetProcessors(); | |
| 151 | 152 | renderActiveTab(); |
| 152 | 153 | }; |
| 30 | 30 | import com.dlsc.formsfx.model.structure.StringField; |
| 31 | 31 | import com.dlsc.preferencesfx.PreferencesFx; |
| 32 | import com.dlsc.preferencesfx.PreferencesFxEvent; | |
| 32 | 33 | import com.dlsc.preferencesfx.model.Category; |
| 33 | 34 | import com.dlsc.preferencesfx.model.Group; |
| 34 | 35 | import com.dlsc.preferencesfx.model.Setting; |
| 35 | 36 | import com.scrivenvar.Services; |
| 36 | 37 | import com.scrivenvar.service.Settings; |
| 37 | 38 | import javafx.beans.property.ObjectProperty; |
| 38 | 39 | import javafx.beans.property.SimpleObjectProperty; |
| 39 | 40 | import javafx.beans.property.SimpleStringProperty; |
| 40 | 41 | import javafx.beans.property.StringProperty; |
| 42 | import javafx.event.EventHandler; | |
| 41 | 43 | import javafx.scene.Node; |
| 42 | 44 | import javafx.scene.control.Label; |
| ... | ||
| 194 | 196 | private Node label( final String key, final boolean interpolate ) { |
| 195 | 197 | return new Label( get( key, interpolate ) ); |
| 198 | } | |
| 199 | ||
| 200 | /** | |
| 201 | * Delegates to the {@link PreferencesFx} event handler for monitoring | |
| 202 | * save events. | |
| 203 | * | |
| 204 | * @param eventHandler The handler to call when the preferences are saved. | |
| 205 | */ | |
| 206 | public void addSaveEventHandler( | |
| 207 | final EventHandler<? super PreferencesFxEvent> eventHandler ) { | |
| 208 | final var eventType = PreferencesFxEvent.EVENT_PREFERENCES_SAVED; | |
| 209 | mPreferencesFx.addEventHandler( eventType, eventHandler ); | |
| 196 | 210 | } |
| 197 | 211 | |
| ... | ||
| 217 | 231 | } |
| 218 | 232 | |
| 219 | private ObjectProperty<File> rDirectoryProperty() { | |
| 233 | public ObjectProperty<File> rDirectoryProperty() { | |
| 220 | 234 | return mPropRDirectory; |
| 221 | 235 | } |
| 222 | 236 | |
| 223 | 237 | public File getRDirectory() { |
| 224 | 238 | return rDirectoryProperty().getValue(); |
| 225 | 239 | } |
| 226 | 240 | |
| 227 | private StringProperty rScriptProperty() { | |
| 241 | public StringProperty rScriptProperty() { | |
| 228 | 242 | return mPropRScript; |
| 229 | 243 | } |
| 32 | 32 | import com.scrivenvar.service.Options; |
| 33 | 33 | import com.scrivenvar.service.events.Notifier; |
| 34 | import javafx.beans.property.ObjectProperty; | |
| 35 | import javafx.beans.property.StringProperty; | |
| 34 | 36 | |
| 35 | 37 | import javax.script.ScriptEngine; |
| 36 | 38 | import javax.script.ScriptEngineManager; |
| 37 | 39 | import javax.script.ScriptException; |
| 40 | import java.io.File; | |
| 38 | 41 | import java.nio.file.Path; |
| 39 | 42 | import java.util.LinkedHashMap; |
| 40 | 43 | import java.util.Map; |
| 44 | import java.util.concurrent.atomic.AtomicBoolean; | |
| 41 | 45 | |
| 42 | 46 | import static com.scrivenvar.Constants.STATUS_PARSE_ERROR; |
| 43 | 47 | import static com.scrivenvar.Messages.get; |
| 44 | 48 | import static com.scrivenvar.decorators.RVariableDecorator.PREFIX; |
| 45 | 49 | import static com.scrivenvar.decorators.RVariableDecorator.SUFFIX; |
| 46 | 50 | import static com.scrivenvar.processors.text.TextReplacementFactory.replace; |
| 47 | 51 | import static java.lang.Math.min; |
| 52 | import static java.lang.String.format; | |
| 48 | 53 | |
| 49 | 54 | /** |
| ... | ||
| 78 | 83 | |
| 79 | 84 | private static final int PREFIX_LENGTH = PREFIX.length(); |
| 85 | ||
| 86 | private final AtomicBoolean mDirty = new AtomicBoolean( false ); | |
| 80 | 87 | |
| 81 | 88 | /** |
| ... | ||
| 89 | 96 | final Map<String, String> map ) { |
| 90 | 97 | super( processor, map ); |
| 98 | ||
| 99 | bootstrapScriptProperty().addListener( | |
| 100 | ( ob, oldScript, newScript ) -> setDirty( true ) ); | |
| 101 | workingDirectoryProperty().addListener( | |
| 102 | ( ob, oldScript, newScript ) -> setDirty( true ) ); | |
| 103 | ||
| 104 | getUserPreferences().addSaveEventHandler( ( handler ) -> { | |
| 105 | if( isDirty() ) { | |
| 106 | init(); | |
| 107 | setDirty( false ); | |
| 108 | } | |
| 109 | } ); | |
| 110 | ||
| 91 | 111 | init(); |
| 92 | 112 | } |
| 93 | 113 | |
| 94 | 114 | /** |
| 95 | * Initialises the R code so that R can find imported libraries. | |
| 115 | * Initialises the R code so that R can find imported libraries. Note that | |
| 116 | * any existing R functionality will not be overwritten if this method is | |
| 117 | * called multiple times. | |
| 96 | 118 | */ |
| 97 | 119 | private void init() { |
| 98 | try { | |
| 99 | final Path wd = getWorkingDirectory(); | |
| 100 | final String dir = wd.toString().replace( '\\', '/' ); | |
| 101 | final Map<String, String> map = getDefinitions(); | |
| 102 | map.put( "$application.r.working.directory$", dir ); | |
| 120 | getNotifier().clear(); | |
| 103 | 121 | |
| 104 | final String bootstrap = getBootstrapScript(); | |
| 122 | try { | |
| 123 | final var bootstrap = getBootstrapScript(); | |
| 105 | 124 | |
| 106 | 125 | if( !bootstrap.isBlank() ) { |
| 126 | final var wd = getWorkingDirectory(); | |
| 127 | final var dir = wd.toString().replace( '\\', '/' ); | |
| 128 | final var map = getDefinitions(); | |
| 129 | map.put( "$application.r.working.directory$", dir ); | |
| 130 | ||
| 107 | 131 | eval( replace( bootstrap, map ) ); |
| 108 | 132 | } |
| 109 | 133 | } catch( final Exception ex ) { |
| 110 | 134 | getNotifier().notify( ex ); |
| 111 | 135 | } |
| 136 | } | |
| 137 | ||
| 138 | /** | |
| 139 | * Sets the dirty flag to indicate that the bootstrap script or working | |
| 140 | * directory has been modified. Upon saving the preferences, if this flag | |
| 141 | * is true, then {@link #init()} will be called to reload the R environment. | |
| 142 | * | |
| 143 | * @param dirty Set to true to reload changes upon closing preferences. | |
| 144 | */ | |
| 145 | private void setDirty( final boolean dirty ) { | |
| 146 | mDirty.set( dirty ); | |
| 147 | } | |
| 148 | ||
| 149 | /** | |
| 150 | * Answers whether R-related settings have been modified. | |
| 151 | * | |
| 152 | * @return {@code true} when the settings have changed. | |
| 153 | */ | |
| 154 | private boolean isDirty() { | |
| 155 | return mDirty.get(); | |
| 112 | 156 | } |
| 113 | 157 | |
| ... | ||
| 123 | 167 | @Override |
| 124 | 168 | public String process( final String text ) { |
| 169 | getNotifier().clear(); | |
| 170 | ||
| 125 | 171 | final int length = text.length(); |
| 126 | 172 | |
| ... | ||
| 197 | 243 | return getScriptEngine().eval( r ); |
| 198 | 244 | } catch( final ScriptException ex ) { |
| 199 | getNotifier().notify( ex ); | |
| 200 | return ""; | |
| 245 | final String expr = r.substring( 0, min( r.length(), 30 ) ); | |
| 246 | final String msg = format( | |
| 247 | "Error with [%s...]: %s", expr, ex.getMessage() ); | |
| 248 | getNotifier().notify( msg ); | |
| 201 | 249 | } |
| 250 | ||
| 251 | return ""; | |
| 202 | 252 | } |
| 203 | 253 | |
| 204 | 254 | /** |
| 205 | * This will return the given path if not null, otherwise it will return | |
| 206 | * the path to the user's directory. | |
| 255 | * Return the given path if not {@code null}, otherwise return the path to | |
| 256 | * the user's directory. | |
| 207 | 257 | * |
| 208 | 258 | * @return A non-null path. |
| 209 | 259 | */ |
| 210 | 260 | private Path getWorkingDirectory() { |
| 211 | 261 | return getUserPreferences().getRDirectory().toPath(); |
| 262 | } | |
| 263 | ||
| 264 | private ObjectProperty<File> workingDirectoryProperty() { | |
| 265 | return getUserPreferences().rDirectoryProperty(); | |
| 212 | 266 | } |
| 213 | 267 | |
| 214 | 268 | /** |
| 215 | 269 | * Loads the R init script from the application's persisted preferences. |
| 216 | 270 | * |
| 217 | * @return A non-null String, possibly empty. | |
| 271 | * @return A non-null string, possibly empty. | |
| 218 | 272 | */ |
| 219 | 273 | private String getBootstrapScript() { |
| 220 | 274 | return getUserPreferences().getRScript(); |
| 275 | } | |
| 276 | ||
| 277 | private StringProperty bootstrapScriptProperty() { | |
| 278 | return getUserPreferences().rScriptProperty(); | |
| 221 | 279 | } |
| 222 | 280 | |
| 25 | 25 | Main.menu.edit.copy=_Copy |
| 26 | 26 | Main.menu.edit.paste=_Paste |
| 27 | Main.menu.edit.selectAll=Select _All | |
| 27 | 28 | Main.menu.edit.find=_Find |
| 28 | 29 | Main.menu.edit.find.next=Find _Next |