Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git
M src/main/java/com/scrivenvar/Constants.java
2929
3030
import com.scrivenvar.service.Settings;
31
import org.fxmisc.flowless.VirtualizedScrollPane;
3231
3332
/**
...
117116
  public static final String PARAGRAPH_ID_PREFIX = "p-";
118117
119
  /**
120
   * The {@link VirtualizedScrollPane} does not provide a way to differentiate
121
   * between keyboard entry triggering a scroll event and a mouse event that
122
   * triggers scrolling. This value ensures that if a user has "just" pressed
123
   * a key that no synchronized scrolling will be attempted, despite a
124
   * scrolling event being fired.
125
   */
126
  public static final int KEYBOARD_SCROLL_DELAY = 750;
118
  public static final String FONT_DIRECTORY = "/fonts";
127119
}
128120
M src/main/java/com/scrivenvar/Main.java
5151
import static java.awt.font.TextAttribute.LIGATURES;
5252
import static java.awt.font.TextAttribute.LIGATURES_ON;
53
import static javafx.scene.input.KeyCode.F11;
54
import static javafx.scene.input.KeyEvent.KEY_PRESSED;
5355
5456
/**
5557
 * Application entry point. The application allows users to edit Markdown
5658
 * files and see a real-time preview of the edits.
5759
 *
5860
 * @author Karl Tauber and White Magic Software, Ltd.
5961
 */
6062
public final class Main extends Application {
6163
62
  // Suppress logging errors to standard output.
64
  // Suppress standard output logging; the Launcher suppresses stderr output.
6365
  static {
6466
    LogManager.getLogManager().reset();
...
109111
    try {
110112
      ResourceWalker.walk(
111
          "/fonts", path -> {
113
          FONT_DIRECTORY, path -> {
112114
            final var uri = path.toUri();
113115
            final var filename = path.toString();
...
158160
    stage.setTitle( getApplicationTitle() );
159161
    stage.setScene( getScene() );
162
163
    stage.addEventHandler( KEY_PRESSED, event -> {
164
      if( F11.equals( event.getCode() ) ) {
165
        stage.setFullScreen( !stage.isFullScreen() );
166
      }
167
    } );
160168
  }
161169
...
179187
    thread.interrupt();
180188
    thread.join();
189
  }
190
191
  private static Notifier getNotifier() {
192
    return sNotifier;
181193
  }
182194
...
191203
  private Options getOptions() {
192204
    return mOptions;
193
  }
194
195
  private static Notifier getNotifier() {
196
    return sNotifier;
197205
  }
198206
M src/main/java/com/scrivenvar/MainWindow.java
4646
import com.scrivenvar.util.ActionBuilder;
4747
import com.scrivenvar.util.ActionUtils;
48
import com.scrivenvar.util.ResourceWalker;
4948
import javafx.application.Platform;
5049
import javafx.beans.binding.Bindings;
...
6362
import javafx.scene.Scene;
6463
import javafx.scene.control.*;
65
import javafx.scene.control.Menu;
66
import javafx.scene.control.MenuBar;
67
import javafx.scene.control.TextField;
6864
import javafx.scene.control.Alert.AlertType;
6965
import javafx.scene.image.Image;
...
8278
import org.xhtmlrenderer.util.XRLog;
8379
84
import java.awt.*;
85
import java.awt.font.TextAttribute;
86
import java.io.FileInputStream;
87
import java.io.IOException;
88
import java.io.InputStream;
89
import java.net.URI;
9080
import java.nio.file.Path;
9181
import java.util.HashMap;
...
10090
import static com.scrivenvar.util.StageState.*;
10191
import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.*;
102
import static java.awt.font.TextAttribute.LIGATURES;
103
import static java.awt.font.TextAttribute.LIGATURES_ON;
10492
import static javafx.event.Event.fireEvent;
10593
import static javafx.scene.input.KeyCode.ENTER;
D src/main/java/com/scrivenvar/controls/BrowseDirectoryButton.java
1
/*
2
 * Copyright (c) 2015 Karl Tauber <karl at jformdesigner dot com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 *  o Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 *
11
 *  o Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
28
package com.scrivenvar.controls;
29
30
import com.scrivenvar.Messages;
31
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
32
import de.jensd.fx.glyphs.fontawesome.utils.FontAwesomeIconFactory;
33
import javafx.event.ActionEvent;
34
import javafx.scene.control.Tooltip;
35
import javafx.stage.DirectoryChooser;
36
37
import java.io.File;
38
39
/**
40
 * Button that opens a directory chooser to select a local directory for a
41
 * URL in markdown.
42
 *
43
 * @author Karl Tauber
44
 */
45
public class BrowseDirectoryButton
46
    extends BrowseFileButton {
47
  public BrowseDirectoryButton() {
48
    setGraphic( FontAwesomeIconFactory.get()
49
                                      .createIcon( FontAwesomeIcon.FOLDER_ALT,
50
                                                   "1.2em" ) );
51
    setTooltip( new Tooltip( Messages.get( "BrowseDirectoryButton.tooltip" ) ) );
52
  }
53
54
  @Override
55
  protected void browse( ActionEvent e ) {
56
    DirectoryChooser directoryChooser = new DirectoryChooser();
57
    directoryChooser.setTitle( Messages.get(
58
        "BrowseDirectoryButton.chooser.title" ) );
59
    directoryChooser.setInitialDirectory( getInitialDirectory() );
60
    File result = directoryChooser.showDialog( getScene().getWindow() );
61
    if( result != null ) {
62
      updateUrl( result );
63
    }
64
  }
65
}
661
M src/main/java/com/scrivenvar/controls/BrowseFileButton.java
2828
package com.scrivenvar.controls;
2929
30
import java.io.File;
31
import java.nio.file.Path;
32
import java.util.ArrayList;
33
import java.util.List;
30
import com.scrivenvar.Messages;
31
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
32
import de.jensd.fx.glyphs.fontawesome.utils.FontAwesomeIconFactory;
3433
import javafx.beans.property.ObjectProperty;
3534
import javafx.beans.property.SimpleObjectProperty;
3635
import javafx.event.ActionEvent;
3736
import javafx.scene.control.Button;
3837
import javafx.scene.control.Tooltip;
3938
import javafx.scene.input.KeyCode;
4039
import javafx.scene.input.KeyEvent;
4140
import javafx.stage.FileChooser;
4241
import javafx.stage.FileChooser.ExtensionFilter;
43
import com.scrivenvar.Messages;
44
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
45
import de.jensd.fx.glyphs.fontawesome.utils.FontAwesomeIconFactory;
42
43
import java.io.File;
44
import java.nio.file.Path;
45
import java.util.ArrayList;
46
import java.util.List;
4647
4748
/**
48
 * Button that opens a file chooser to select a local file for a URL in markdown.
49
 * Button that opens a file chooser to select a local file for a URL in
50
 * markdown.
4951
 *
5052
 * @author Karl Tauber
5153
 */
52
public class BrowseFileButton
53
	extends Button
54
{
55
	private final List<ExtensionFilter> extensionFilters = new ArrayList<>();
54
public class BrowseFileButton extends Button {
55
  private final List<ExtensionFilter> extensionFilters = new ArrayList<>();
5656
57
	public BrowseFileButton() {
58
		setGraphic(FontAwesomeIconFactory.get().createIcon(FontAwesomeIcon.FILE_ALT, "1.2em"));
59
		setTooltip(new Tooltip(Messages.get("BrowseFileButton.tooltip")));
60
		setOnAction(this::browse);
57
  public BrowseFileButton() {
58
    setGraphic(
59
        FontAwesomeIconFactory.get().createIcon( FontAwesomeIcon.FILE_ALT )
60
    );
61
    setTooltip( new Tooltip( Messages.get( "BrowseFileButton.tooltip" ) ) );
62
    setOnAction( this::browse );
6163
62
		disableProperty().bind(basePath.isNull());
64
    disableProperty().bind( basePath.isNull() );
6365
64
		// workaround for a JavaFX bug:
65
		//   avoid closing the dialog that contains this control when the user
66
		//   closes the FileChooser or DirectoryChooser using the ESC key
67
		addEventHandler(KeyEvent.KEY_RELEASED, e-> {
68
			if (e.getCode() == KeyCode.ESCAPE)
69
				e.consume();
70
		});
71
	}
66
    // workaround for a JavaFX bug:
67
    //   avoid closing the dialog that contains this control when the user
68
    //   closes the FileChooser or DirectoryChooser using the ESC key
69
    addEventHandler( KeyEvent.KEY_RELEASED, e -> {
70
      if( e.getCode() == KeyCode.ESCAPE ) {
71
        e.consume();
72
      }
73
    } );
74
  }
7275
73
	public void addExtensionFilter(ExtensionFilter extensionFilter) {
74
		extensionFilters.add(extensionFilter);
75
	}
76
  public void addExtensionFilter( ExtensionFilter extensionFilter ) {
77
    extensionFilters.add( extensionFilter );
78
  }
7679
77
	// 'basePath' property
78
	private final ObjectProperty<Path> basePath = new SimpleObjectProperty<>();
79
	public Path getBasePath() { return basePath.get(); }
80
	public void setBasePath(Path basePath) { this.basePath.set(basePath); }
81
	public ObjectProperty<Path> basePathProperty() { return basePath; }
80
  // 'basePath' property
81
  private final ObjectProperty<Path> basePath = new SimpleObjectProperty<>();
8282
83
	// 'url' property
84
	private final ObjectProperty<String> url = new SimpleObjectProperty<>();
85
	public String getUrl() { return url.get(); }
86
	public void setUrl(String url) { this.url.set(url); }
87
	public ObjectProperty<String> urlProperty() { return url; }
83
  public Path getBasePath() {
84
    return basePath.get();
85
  }
8886
89
	protected void browse(ActionEvent e) {
90
		FileChooser fileChooser = new FileChooser();
91
		fileChooser.setTitle(Messages.get("BrowseFileButton.chooser.title"));
92
		fileChooser.getExtensionFilters().addAll(extensionFilters);
93
		fileChooser.getExtensionFilters().add(new ExtensionFilter(Messages.get("BrowseFileButton.chooser.allFilesFilter"), "*.*"));
94
		fileChooser.setInitialDirectory(getInitialDirectory());
95
		File result = fileChooser.showOpenDialog(getScene().getWindow());
96
		if (result != null)
97
			updateUrl(result);
98
	}
87
  public void setBasePath( Path basePath ) {
88
    this.basePath.set( basePath );
89
  }
9990
100
	protected File getInitialDirectory() {
101
		//TODO build initial directory based on current value of 'url' property
102
		return getBasePath().toFile();
103
	}
91
  // 'url' property
92
  private final ObjectProperty<String> url = new SimpleObjectProperty<>();
10493
105
	protected void updateUrl(File file) {
106
		String newUrl;
107
		try {
108
			newUrl = getBasePath().relativize(file.toPath()).toString();
109
		} catch (IllegalArgumentException ex) {
110
			newUrl = file.toString();
111
		}
112
		url.set(newUrl.replace('\\', '/'));
113
	}
94
  public ObjectProperty<String> urlProperty() {
95
    return url;
96
  }
97
98
  protected void browse( ActionEvent e ) {
99
    FileChooser fileChooser = new FileChooser();
100
    fileChooser.setTitle( Messages.get( "BrowseFileButton.chooser.title" ) );
101
    fileChooser.getExtensionFilters().addAll( extensionFilters );
102
    fileChooser.getExtensionFilters()
103
               .add( new ExtensionFilter( Messages.get(
104
                   "BrowseFileButton.chooser.allFilesFilter" ), "*.*" ) );
105
    fileChooser.setInitialDirectory( getInitialDirectory() );
106
    File result = fileChooser.showOpenDialog( getScene().getWindow() );
107
    if( result != null ) {
108
      updateUrl( result );
109
    }
110
  }
111
112
  protected File getInitialDirectory() {
113
    //TODO build initial directory based on current value of 'url' property
114
    return getBasePath().toFile();
115
  }
116
117
  protected void updateUrl( File file ) {
118
    String newUrl;
119
    try {
120
      newUrl = getBasePath().relativize( file.toPath() ).toString();
121
    } catch( IllegalArgumentException ex ) {
122
      newUrl = file.toString();
123
    }
124
    url.set( newUrl.replace( '\\', '/' ) );
125
  }
114126
}
115127
D src/main/java/com/scrivenvar/controls/FlagCheckBox.java
1
/*
2
 * Copyright (c) 2015 Karl Tauber <karl at jformdesigner dot com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 *  o Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 *
11
 *  o Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
package com.scrivenvar.controls;
28
29
import javafx.beans.property.IntegerProperty;
30
import javafx.beans.property.SimpleIntegerProperty;
31
import javafx.scene.control.CheckBox;
32
33
/**
34
 * CheckBox that toggles a bit in an integer.
35
 *
36
 * @author Karl Tauber
37
 */
38
public class FlagCheckBox extends CheckBox {
39
40
  public FlagCheckBox() {
41
    setOnAction( e -> {
42
      if( isSelected() ) {
43
        setFlags( getFlags() | getFlag() );
44
      } else {
45
        setFlags( getFlags() & ~getFlag() );
46
      }
47
    } );
48
49
    flags.addListener( (obs, oldFlags, newFlags) -> {
50
      setSelected( (newFlags.intValue() & getFlag()) != 0 );
51
    } );
52
  }
53
54
  // 'flag' property
55
  private final IntegerProperty flag = new SimpleIntegerProperty();
56
57
  public int getFlag() {
58
    return flag.get();
59
  }
60
61
  public void setFlag( int flag ) {
62
    this.flag.set( flag );
63
  }
64
65
  public IntegerProperty flagProperty() {
66
    return flag;
67
  }
68
69
  // 'flags' property
70
  private final IntegerProperty flags = new SimpleIntegerProperty();
71
72
  public int getFlags() {
73
    return flags.get();
74
  }
75
76
  public void setFlags( int flags ) {
77
    this.flags.set( flags );
78
  }
79
80
  public IntegerProperty flagsProperty() {
81
    return flags;
82
  }
83
}
841
M src/main/java/com/scrivenvar/definition/DefinitionPane.java
2828
package com.scrivenvar.definition;
2929
30
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
31
import de.jensd.fx.glyphs.fontawesome.utils.FontAwesomeIconFactory;
3032
import javafx.beans.property.SimpleStringProperty;
3133
import javafx.beans.property.StringProperty;
3234
import javafx.collections.ObservableList;
35
import javafx.event.ActionEvent;
3336
import javafx.event.Event;
3437
import javafx.event.EventHandler;
38
import javafx.geometry.Insets;
3539
import javafx.scene.Node;
3640
import javafx.scene.control.*;
3741
import javafx.scene.control.cell.TextFieldTreeCell;
3842
import javafx.scene.input.KeyEvent;
43
import javafx.scene.layout.BorderPane;
44
import javafx.scene.layout.HBox;
3945
import javafx.util.StringConverter;
4046
4147
import java.util.*;
4248
4349
import static com.scrivenvar.Messages.get;
50
import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.*;
51
import static javafx.geometry.Pos.CENTER;
4452
import static javafx.scene.input.KeyEvent.KEY_PRESSED;
4553
...
8593
    treeView.setShowRoot( false );
8694
    getSelectionModel().setSelectionMode( SelectionMode.MULTIPLE );
95
96
    final var bCreate = createButton(
97
        "create", TREE, e -> addItem() );
98
    final var bRename = createButton(
99
        "rename", EDIT, e -> editSelectedItem() );
100
    final var bDelete = createButton(
101
        "delete", TRASH, e -> deleteSelectedItems() );
102
103
    final var buttonBar = new HBox();
104
    buttonBar.getChildren().addAll( bCreate, bRename, bDelete );
105
    buttonBar.setAlignment( CENTER );
106
    buttonBar.setSpacing( 10 );
107
108
    final var borderPane = new BorderPane();
109
    borderPane.setPadding( new Insets( 0, 0, 0, 0 ) );
110
    borderPane.setCenter( treeView );
111
    borderPane.setBottom( buttonBar );
87112
88113
    textProperty().bind( mFilename );
89114
90
    setContent( treeView );
115
    setContent( borderPane );
91116
    setCollapsible( false );
117
  }
118
119
  private Button createButton(
120
      final String msgKey,
121
      final FontAwesomeIcon icon,
122
      final EventHandler<ActionEvent> eventHandler ) {
123
    final var keyPrefix = "Pane.definition.button." + msgKey;
124
    final var button = new Button( get( keyPrefix + ".label" ) );
125
    button.setOnAction( eventHandler );
126
127
    button.setGraphic(
128
        FontAwesomeIconFactory.get().createIcon( icon )
129
    );
130
    button.setTooltip( new Tooltip( get( keyPrefix + ".tooltip" ) ) );
131
132
    return button;
92133
  }
93134
M src/main/resources/com/scrivenvar/messages.properties
130130
131131
Pane.definition.node.root.title=Definitions
132
Pane.definition.button.create.label=_Create
133
Pane.definition.button.rename.label=_Rename
134
Pane.definition.button.delete.label=_Delete
135
Pane.definition.button.create.tooltip=Add new item (Insert)
136
Pane.definition.button.rename.tooltip=Rename selected item (F2)
137
Pane.definition.button.delete.tooltip=Delete selected items (Delete)
132138
133139
# Controls ###############################################################
M src/main/resources/com/scrivenvar/preview/webview.css
4040
=============================================================================*/
4141
body {
42
  /* Must be bundled in JAR file. */
4243
  font-family: "Vollkorn", serif;
4344
  font-size: 16px;
...
146147
=============================================================================*/
147148
dl {
148
  /** Horizontal scroll bar from appears if set to 100%. */
149
  /** Horizontal scroll bar will appear if set to 100%. */
149150
  width: 99%;
150151
  overflow: hidden;
...
177178
pre, code, tt {
178179
  font-size: 14px;
180
  /* Must be bundled in JAR file. */
179181
  font-family: "Fira Code", monospace;
180182
}
...
209211
kbd {
210212
  background-color: #ccc;
211
  background-image: linear-gradient(#F1F1F1, #DDDDDD);
213
  background-image: linear-gradient(#f8f8f8, #DDDDDD);
212214
  background-repeat: repeat-x;
213215
  border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
214216
  border-image: none;
215217
  border-radius: 2px;
216218
  border-style: solid;
217219
  border-width: 1px;
218
  font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
219
  line-height: 10px;
220
  font-family: sans-serif;
221
  line-height: 1.25;
220222
  padding: 1px 4px;
221223
}