Cleaned up the user interface a lot.
This commit is contained in:
parent
ec06cf56cf
commit
392114e3e6
8
pom.xml
8
pom.xml
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<groupId>nl.andrewlalis</groupId>
|
<groupId>nl.andrewlalis</groupId>
|
||||||
<artifactId>BlockBookBinder</artifactId>
|
<artifactId>BlockBookBinder</artifactId>
|
||||||
<version>1.1.0</version>
|
<version>1.2.0</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>12</maven.compiler.source>
|
<maven.compiler.source>12</maven.compiler.source>
|
||||||
|
@ -62,5 +62,11 @@
|
||||||
<artifactId>flatlaf</artifactId>
|
<artifactId>flatlaf</artifactId>
|
||||||
<version>1.0-rc3</version>
|
<version>1.0-rc3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-model</artifactId>
|
||||||
|
<version>3.6.3</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
|
@ -1,6 +1,7 @@
|
||||||
package nl.andrewlalis.blockbookbinder;
|
package nl.andrewlalis.blockbookbinder;
|
||||||
|
|
||||||
import com.formdev.flatlaf.FlatDarkLaf;
|
import com.formdev.flatlaf.FlatDarkLaf;
|
||||||
|
import nl.andrewlalis.blockbookbinder.util.VersionReader;
|
||||||
import nl.andrewlalis.blockbookbinder.view.MainFrame;
|
import nl.andrewlalis.blockbookbinder.view.MainFrame;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -9,6 +10,8 @@ import javax.swing.*;
|
||||||
* The main class for the application.
|
* The main class for the application.
|
||||||
*/
|
*/
|
||||||
public class BlockBookBinder {
|
public class BlockBookBinder {
|
||||||
|
public static final String VERSION = VersionReader.getVersion();
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
FlatDarkLaf.install();
|
FlatDarkLaf.install();
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
package nl.andrewlalis.blockbookbinder.control;
|
|
||||||
|
|
||||||
import nl.andrewlalis.blockbookbinder.model.build.BookBuilder;
|
|
||||||
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
|
|
||||||
import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
|
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action listener that, when activated, converts the text from the source panel
|
|
||||||
* into a formatted book.
|
|
||||||
*/
|
|
||||||
public class ConvertToBookActionListener implements ActionListener {
|
|
||||||
private final SourceTextPanel sourceTextPanel;
|
|
||||||
private final BookPreviewPanel bookPreviewPanel;
|
|
||||||
|
|
||||||
public ConvertToBookActionListener(SourceTextPanel sourceTextPanel, BookPreviewPanel bookPreviewPanel) {
|
|
||||||
this.sourceTextPanel = sourceTextPanel;
|
|
||||||
this.bookPreviewPanel = bookPreviewPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
this.bookPreviewPanel.setBook(
|
|
||||||
new BookBuilder().build(this.sourceTextPanel.getSourceText())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package nl.andrewlalis.blockbookbinder.control;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
|
|
||||||
public class ImportAction extends AbstractAction {
|
|
||||||
public ImportAction(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImportAction(String name, Icon icon) {
|
|
||||||
super(name, icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
System.out.println("Import!");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package nl.andrewlalis.blockbookbinder.control.export;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import nl.andrewlalis.blockbookbinder.model.Book;
|
||||||
|
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
|
||||||
|
import nl.andrewlalis.blockbookbinder.view.export.ExportToBookDialog;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
public class ExportBookToMinecraftAction extends AbstractAction {
|
||||||
|
@Getter
|
||||||
|
private static final ExportBookToMinecraftAction instance = new ExportBookToMinecraftAction();
|
||||||
|
|
||||||
|
public ExportBookToMinecraftAction() {
|
||||||
|
super("Export to Minecraft");
|
||||||
|
this.putValue(SHORT_DESCRIPTION, "Export the current book to Minecraft.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private BookPreviewPanel bookPreviewPanel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
final Book book = bookPreviewPanel.getBook();
|
||||||
|
if (book == null || book.getPageCount() == 0) {
|
||||||
|
JOptionPane.showMessageDialog(
|
||||||
|
this.bookPreviewPanel.getRootPane(),
|
||||||
|
"Cannot export an empty book.",
|
||||||
|
"Empty Book",
|
||||||
|
JOptionPane.WARNING_MESSAGE
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ExportToBookDialog dialog = new ExportToBookDialog(SwingUtilities.getWindowAncestor(this.bookPreviewPanel), bookPreviewPanel.getBook());
|
||||||
|
dialog.setupAndShow();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,22 @@
|
||||||
package nl.andrewlalis.blockbookbinder.control;
|
package nl.andrewlalis.blockbookbinder.control.source;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
|
import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
/**
|
public class CleanSourceAction extends AbstractAction {
|
||||||
* Listener for an action where the source text is 'cleaned' by removing any
|
@Getter
|
||||||
* trailing whitespace, and removing unnecessary newlines.
|
private final static CleanSourceAction instance = new CleanSourceAction();
|
||||||
*/
|
|
||||||
public class CleanSourceActionListener implements ActionListener {
|
|
||||||
private final SourceTextPanel sourceTextPanel;
|
|
||||||
|
|
||||||
public CleanSourceActionListener(SourceTextPanel sourceTextPanel) {
|
@Setter
|
||||||
this.sourceTextPanel = sourceTextPanel;
|
private SourceTextPanel sourceTextPanel;
|
||||||
|
|
||||||
|
public CleanSourceAction() {
|
||||||
|
super("Clean Source");
|
||||||
|
this.putValue(SHORT_DESCRIPTION, "Clean up the source text.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -0,0 +1,32 @@
|
||||||
|
package nl.andrewlalis.blockbookbinder.control.source;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import nl.andrewlalis.blockbookbinder.model.build.BookBuilder;
|
||||||
|
import nl.andrewlalis.blockbookbinder.view.SourceTextPanel;
|
||||||
|
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
public class CompileFromSourceAction extends AbstractAction {
|
||||||
|
@Getter
|
||||||
|
private static final CompileFromSourceAction instance = new CompileFromSourceAction();
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private SourceTextPanel sourceTextPanel;
|
||||||
|
@Setter
|
||||||
|
private BookPreviewPanel bookPreviewPanel;
|
||||||
|
|
||||||
|
public CompileFromSourceAction() {
|
||||||
|
super("Compile From Source");
|
||||||
|
this.putValue(SHORT_DESCRIPTION, "Compile the current source text into a book.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
this.bookPreviewPanel.setBook(
|
||||||
|
new BookBuilder().build(this.sourceTextPanel.getSourceText())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package nl.andrewlalis.blockbookbinder.control.source;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
public class ImportSourceAction extends AbstractAction {
|
||||||
|
@Getter
|
||||||
|
private static final ImportSourceAction instance = new ImportSourceAction();
|
||||||
|
|
||||||
|
public ImportSourceAction() {
|
||||||
|
super("Import Source");
|
||||||
|
this.putValue(SHORT_DESCRIPTION, "Import source text from a file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package nl.andrewlalis.blockbookbinder.util;
|
||||||
|
|
||||||
|
import org.apache.maven.model.Model;
|
||||||
|
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||||
|
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
public class VersionReader {
|
||||||
|
public static String getVersion() {
|
||||||
|
MavenXpp3Reader reader = new MavenXpp3Reader();
|
||||||
|
try {
|
||||||
|
Model model;
|
||||||
|
if ((new File("pom.xml")).exists()) {
|
||||||
|
model = reader.read(new FileReader("pom.xml"));
|
||||||
|
} else {
|
||||||
|
model = reader.read(new InputStreamReader(
|
||||||
|
VersionReader.class.getResourceAsStream("/META-INF/maven/nl.andrewlalis/BlockBookBinder/pom.xml")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return model.getVersion();
|
||||||
|
} catch (IOException | XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package nl.andrewlalis.blockbookbinder.view;
|
package nl.andrewlalis.blockbookbinder.view;
|
||||||
|
|
||||||
import nl.andrewlalis.blockbookbinder.model.Book;
|
import nl.andrewlalis.blockbookbinder.BlockBookBinder;
|
||||||
|
import nl.andrewlalis.blockbookbinder.control.export.ExportBookToMinecraftAction;
|
||||||
|
import nl.andrewlalis.blockbookbinder.control.source.CleanSourceAction;
|
||||||
|
import nl.andrewlalis.blockbookbinder.control.source.CompileFromSourceAction;
|
||||||
|
import nl.andrewlalis.blockbookbinder.control.source.ImportSourceAction;
|
||||||
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
|
import nl.andrewlalis.blockbookbinder.util.ApplicationProperties;
|
||||||
import nl.andrewlalis.blockbookbinder.view.about.AboutDialog;
|
import nl.andrewlalis.blockbookbinder.view.about.AboutDialog;
|
||||||
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
|
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
|
||||||
import nl.andrewlalis.blockbookbinder.view.export.ExportToBookDialog;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
@ -19,7 +22,7 @@ public class MainFrame extends JFrame {
|
||||||
ApplicationProperties.getIntProp("frame.default_width"),
|
ApplicationProperties.getIntProp("frame.default_width"),
|
||||||
ApplicationProperties.getIntProp("frame.default_height")
|
ApplicationProperties.getIntProp("frame.default_height")
|
||||||
));
|
));
|
||||||
this.setTitle(ApplicationProperties.getProp("frame.title"));
|
this.setTitle(ApplicationProperties.getProp("frame.title") + " Version " + BlockBookBinder.VERSION);
|
||||||
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||||
final URL iconUrl = this.getClass().getClassLoader().getResource("images/book_and_quill.png");
|
final URL iconUrl = this.getClass().getClassLoader().getResource("images/book_and_quill.png");
|
||||||
if (iconUrl != null) {
|
if (iconUrl != null) {
|
||||||
|
@ -34,15 +37,41 @@ public class MainFrame extends JFrame {
|
||||||
this.setVisible(true);
|
this.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Container buildContentPane() {
|
||||||
|
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||||
|
JPanel doublePanel = new JPanel(new GridLayout(1, 2));
|
||||||
|
|
||||||
|
BookPreviewPanel bookPreviewPanel = new BookPreviewPanel();
|
||||||
|
doublePanel.add(bookPreviewPanel);
|
||||||
|
CompileFromSourceAction.getInstance().setBookPreviewPanel(bookPreviewPanel);
|
||||||
|
ExportBookToMinecraftAction.getInstance().setBookPreviewPanel(bookPreviewPanel);
|
||||||
|
|
||||||
|
SourceTextPanel sourceTextPanel = new SourceTextPanel();
|
||||||
|
doublePanel.add(sourceTextPanel);
|
||||||
|
CompileFromSourceAction.getInstance().setSourceTextPanel(sourceTextPanel);
|
||||||
|
CleanSourceAction.getInstance().setSourceTextPanel(sourceTextPanel);
|
||||||
|
|
||||||
|
mainPanel.add(doublePanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return mainPanel;
|
||||||
|
}
|
||||||
|
|
||||||
private JMenuBar buildMenuBar() {
|
private JMenuBar buildMenuBar() {
|
||||||
JMenuBar menuBar = new JMenuBar();
|
JMenuBar menuBar = new JMenuBar();
|
||||||
|
|
||||||
JMenu fileMenu = new JMenu("File");
|
JMenu fileMenu = new JMenu("File");
|
||||||
|
fileMenu.add(ImportSourceAction.getInstance());
|
||||||
JMenuItem exitItem = new JMenuItem("Exit");
|
JMenuItem exitItem = new JMenuItem("Exit");
|
||||||
exitItem.addActionListener(e -> this.dispose());
|
exitItem.addActionListener(e -> this.dispose());
|
||||||
fileMenu.add(exitItem);
|
fileMenu.add(exitItem);
|
||||||
menuBar.add(fileMenu);
|
menuBar.add(fileMenu);
|
||||||
|
|
||||||
|
JMenu bookMenu = new JMenu("Book");
|
||||||
|
bookMenu.add(CompileFromSourceAction.getInstance());
|
||||||
|
bookMenu.add(CleanSourceAction.getInstance());
|
||||||
|
bookMenu.add(ExportBookToMinecraftAction.getInstance());
|
||||||
|
menuBar.add(bookMenu);
|
||||||
|
|
||||||
JMenu helpMenu = new JMenu("Help");
|
JMenu helpMenu = new JMenu("Help");
|
||||||
JMenuItem aboutItem = new JMenuItem("About");
|
JMenuItem aboutItem = new JMenuItem("About");
|
||||||
aboutItem.addActionListener(e -> {
|
aboutItem.addActionListener(e -> {
|
||||||
|
@ -55,36 +84,6 @@ public class MainFrame extends JFrame {
|
||||||
return menuBar;
|
return menuBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Container buildContentPane() {
|
|
||||||
JPanel mainPanel = new JPanel(new BorderLayout());
|
|
||||||
|
|
||||||
JPanel doublePanel = new JPanel(new GridLayout(1, 2));
|
|
||||||
BookPreviewPanel bookPreviewPanel = new BookPreviewPanel();
|
|
||||||
doublePanel.add(bookPreviewPanel);
|
|
||||||
SourceTextPanel sourceTextPanel = new SourceTextPanel(bookPreviewPanel);
|
|
||||||
doublePanel.add(sourceTextPanel);
|
|
||||||
mainPanel.add(doublePanel, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
||||||
JButton exportButton = new JButton("Export to Book");
|
|
||||||
JButton cancelExportButton = new JButton("Cancel Export");
|
|
||||||
cancelExportButton.setEnabled(false);
|
|
||||||
exportButton.addActionListener(e -> {
|
|
||||||
final Book book = bookPreviewPanel.getBook();
|
|
||||||
if (book == null || book.getPageCount() == 0) {
|
|
||||||
JOptionPane.showMessageDialog(this, "Cannot export an empty book.", "Empty Book", JOptionPane.WARNING_MESSAGE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ExportToBookDialog dialog = new ExportToBookDialog(this, bookPreviewPanel.getBook());
|
|
||||||
dialog.setupAndShow();
|
|
||||||
});
|
|
||||||
bottomPanel.add(exportButton);
|
|
||||||
bottomPanel.add(cancelExportButton);
|
|
||||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
return mainPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package nl.andrewlalis.blockbookbinder.view;
|
package nl.andrewlalis.blockbookbinder.view;
|
||||||
|
|
||||||
import nl.andrewlalis.blockbookbinder.control.CleanSourceActionListener;
|
|
||||||
import nl.andrewlalis.blockbookbinder.control.ConvertToBookActionListener;
|
|
||||||
import nl.andrewlalis.blockbookbinder.view.book.BookPreviewPanel;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
@ -15,7 +11,7 @@ import java.awt.*;
|
||||||
public class SourceTextPanel extends JPanel {
|
public class SourceTextPanel extends JPanel {
|
||||||
private final JTextArea textArea;
|
private final JTextArea textArea;
|
||||||
|
|
||||||
public SourceTextPanel(BookPreviewPanel bookPreviewPanel) {
|
public SourceTextPanel() {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
|
||||||
this.add(new JLabel("Source Text"), BorderLayout.NORTH);
|
this.add(new JLabel("Source Text"), BorderLayout.NORTH);
|
||||||
|
@ -26,17 +22,6 @@ public class SourceTextPanel extends JPanel {
|
||||||
this.textArea.setLineWrap(true);
|
this.textArea.setLineWrap(true);
|
||||||
JScrollPane scrollWrappedMainTextArea = new JScrollPane(this.textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
JScrollPane scrollWrappedMainTextArea = new JScrollPane(this.textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||||
this.add(scrollWrappedMainTextArea, BorderLayout.CENTER);
|
this.add(scrollWrappedMainTextArea, BorderLayout.CENTER);
|
||||||
|
|
||||||
JPanel rightPanelButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
||||||
this.add(rightPanelButtonPanel, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
JButton convertButton = new JButton("Convert to Book");
|
|
||||||
convertButton.addActionListener(new ConvertToBookActionListener(this, bookPreviewPanel));
|
|
||||||
rightPanelButtonPanel.add(convertButton);
|
|
||||||
|
|
||||||
JButton cleanButton = new JButton("Clean");
|
|
||||||
cleanButton.addActionListener(new CleanSourceActionListener(this));
|
|
||||||
rightPanelButtonPanel.add(cleanButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSourceText() {
|
public String getSourceText() {
|
||||||
|
|
|
@ -31,8 +31,8 @@ public class ExportToBookDialog extends JDialog {
|
||||||
private Thread exporterThread;
|
private Thread exporterThread;
|
||||||
private BookExporter exporterRunnable;
|
private BookExporter exporterRunnable;
|
||||||
|
|
||||||
public ExportToBookDialog(Frame owner, Book book) {
|
public ExportToBookDialog(Window owner, Book book) {
|
||||||
super(owner, "Export to Book", true);
|
super(owner, "Export to Book");
|
||||||
this.book = book;
|
this.book = book;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
version=1.2.0
|
||||||
|
|
||||||
# Settings for the application's GUI.
|
# Settings for the application's GUI.
|
||||||
frame.title=Block Book Binder
|
frame.title=Block Book Binder
|
||||||
frame.default_width=800
|
frame.default_width=800
|
||||||
|
|
Loading…
Reference in New Issue