Added a dialog to generate boilerplate metadata for new mods

This commit is contained in:
Vhati 2017-12-10 20:23:31 -05:00
parent 3baf16db6f
commit 80c6bd812b
3 changed files with 359 additions and 0 deletions

View file

@ -0,0 +1,181 @@
package net.vhati.modmanager.ui;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import net.vhati.modmanager.ui.FieldEditorPanel;
import net.vhati.modmanager.ui.FieldEditorPanel.ContentType;
import net.vhati.modmanager.ui.RegexDocument;
import net.vhati.modmanager.xml.JDOMModMetadataWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class CreateModDialog extends JDialog implements ActionListener {
private static final Logger log = LogManager.getLogger( CreateModDialog.class );
protected static final String DIR_NAME = "Directory Name";
protected static final String TITLE = "Title";
protected static final String URL = "Thread URL";
protected static final String AUTHOR = "Author";
protected static final String VERSION = "Version";
protected static final String DESC = "Description";
protected FieldEditorPanel editorPanel;
protected JButton applyBtn;
protected File modsDir;
public CreateModDialog( Frame owner, File modsDir ) {
super( owner, "New Mod" );
this.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
this.modsDir = modsDir;
editorPanel = new FieldEditorPanel( false );
editorPanel.setBorder( BorderFactory.createEmptyBorder( 10, 10, 0, 10 ) );
editorPanel.setNameWidth( 100 );
editorPanel.setValueWidth( 350 );
editorPanel.addRow( DIR_NAME, ContentType.STRING );
editorPanel.getString( DIR_NAME ).setDocument( new RegexDocument( "[^\\/:;*?<>|^\"]*" ) );
editorPanel.addTextRow( String.format( "The name of a directory to create in the %s/ folder.", modsDir.getName() ) );
editorPanel.addSeparatorRow();
editorPanel.addRow( TITLE, ContentType.STRING );
editorPanel.addTextRow( "The title of this mod." );
editorPanel.addSeparatorRow();
editorPanel.addRow( URL, ContentType.STRING );
editorPanel.addTextRow( "This mod's thread on the forum." );
editorPanel.addSeparatorRow();
editorPanel.addRow( AUTHOR, ContentType.STRING );
editorPanel.addTextRow( "Your forum user name." );
editorPanel.addSeparatorRow();
editorPanel.addRow( VERSION, ContentType.STRING );
editorPanel.addTextRow( "The revision/variant of this release, preferably at least a number." );
editorPanel.addSeparatorRow();
editorPanel.addRow( DESC, ContentType.TEXT_AREA );
editorPanel.getTextArea( DESC ).setRows( 15 );
editorPanel.addTextRow( "Summary of gameplay effects; flavor; features; concerns about compatibility, preferred order, requirements; replaced ship slot; etc." );
JPanel ctrlPanel = new JPanel();
ctrlPanel.setLayout( new BoxLayout( ctrlPanel, BoxLayout.X_AXIS ) );
ctrlPanel.setBorder( BorderFactory.createEmptyBorder( 10, 0, 10, 0 ) );
ctrlPanel.add( Box.createHorizontalGlue() );
applyBtn = new JButton( "Generate Mod" );
applyBtn.addActionListener( this );
ctrlPanel.add( applyBtn );
ctrlPanel.add( Box.createHorizontalGlue() );
final JScrollPane editorScroll = new JScrollPane( editorPanel );
editorScroll.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS );
editorScroll.getVerticalScrollBar().setUnitIncrement( 10 );
int vbarWidth = editorScroll.getVerticalScrollBar().getPreferredSize().width;
editorScroll.setPreferredSize( new Dimension( editorPanel.getPreferredSize().width+vbarWidth+5, 400 ) );
JPanel contentPane = new JPanel( new BorderLayout() );
contentPane.add( editorScroll, BorderLayout.CENTER );
contentPane.add( ctrlPanel, BorderLayout.SOUTH );
this.setContentPane( contentPane );
this.pack();
this.setMinimumSize( new Dimension( 250, 250 ) );
editorScroll.addAncestorListener(new AncestorListener() {
@Override
public void ancestorAdded( AncestorEvent e ) {
editorScroll.getViewport().setViewPosition( new Point( 0, 0 ) );
}
@Override
public void ancestorMoved( AncestorEvent e ) {
}
@Override
public void ancestorRemoved( AncestorEvent e ) {
}
});
}
@Override
public void actionPerformed( ActionEvent e ) {
Object source = e.getSource();
if ( source == applyBtn ) {
String dirName = editorPanel.getString( DIR_NAME ).getText().trim();
String modTitle = editorPanel.getString( TITLE ).getText().trim();
String modURL = editorPanel.getString( URL ).getText().trim();
String modAuthor = editorPanel.getString( AUTHOR ).getText().trim();
String modVersion = editorPanel.getString( VERSION ).getText().trim();
String modDesc = editorPanel.getTextArea( DESC ).getText().trim();
if ( dirName.length() == 0 ) {
JOptionPane.showMessageDialog( CreateModDialog.this, "No directory name was given.", "Nothing to do", JOptionPane.WARNING_MESSAGE );
return;
}
File genDir = new File( modsDir, dirName );
if ( !genDir.exists() ) {
try {
// Generate the mod.
if ( genDir.mkdir() ) {
File appendixDir = new File ( genDir, "mod-appendix" );
if ( appendixDir.mkdir() ) {
File metadataFile = new File( appendixDir, "metadata.xml" );
JDOMModMetadataWriter.writeMetadata( metadataFile, modTitle, modURL, modAuthor, modVersion, modDesc );
}
else {
throw new IOException( String.format( "Failed to create directory: %s", appendixDir.getName() ) );
}
}
else {
throw new IOException( String.format( "Failed to create directory: %s", genDir.getName() ) );
}
// Show the folder.
try {
if ( Desktop.isDesktopSupported() ) {
Desktop.getDesktop().open( genDir.getCanonicalFile() );
} else {
log.error( String.format( "Java cannot open the %s/ folder for you on this OS", genDir.getName() ) );
}
}
catch ( IOException f ) {
log.error( String.format( "Error opening %s/ folder", genDir.getName() ), f );
}
// All done.
CreateModDialog.this.dispose();
}
catch ( IOException f ) {
log.error( String.format( "Failed to generate new mod: %s", genDir.getName() ), f );
JOptionPane.showMessageDialog( CreateModDialog.this, String.format( "Failed to generate new mod: %s\n%s", genDir.getName(), f.getMessage() ), "Error", JOptionPane.ERROR_MESSAGE );
}
}
else {
JOptionPane.showMessageDialog( CreateModDialog.this, String.format( "A directory named \"%s\" already exists.", genDir.getName() ), "Nothing to do", JOptionPane.WARNING_MESSAGE );
}
}
}
}

View file

@ -130,6 +130,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
private JMenu fileMenu;
private JMenuItem rescanMenuItem;
private JMenuItem extractDatsMenuItem;
private JMenuItem createModMenuItem;
private JMenuItem sandboxMenuItem;
private JMenuItem configMenuItem;
private JMenuItem exitMenuItem;
@ -318,10 +319,16 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
extractDatsMenuItem.addMouseListener( new StatusbarMouseListener( this, "Extract FTL resources into a folder." ) );
extractDatsMenuItem.addActionListener( this );
fileMenu.add( extractDatsMenuItem );
fileMenu.add( new JSeparator() );
createModMenuItem = new JMenuItem( "New Mod..." );
createModMenuItem.addMouseListener( new StatusbarMouseListener( this, "Generate boilerplace for a new mod." ) );
createModMenuItem.addActionListener( this );
fileMenu.add( createModMenuItem );
sandboxMenuItem = new JMenuItem( "XML Sandbox..." );
sandboxMenuItem.addMouseListener( new StatusbarMouseListener( this, "Experiment with advanced mod syntax." ) );
sandboxMenuItem.addActionListener( this );
fileMenu.add( sandboxMenuItem );
fileMenu.add( new JSeparator() );
configMenuItem = new JMenuItem( "Preferences..." );
configMenuItem.addMouseListener( new StatusbarMouseListener( this, "Edit preferences." ) );
configMenuItem.addActionListener( this );
@ -803,6 +810,15 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
extractDlg.extract();
extractDlg.setVisible( true );
}
else if ( source == createModMenuItem ) {
setStatusText( "" );
CreateModDialog createModDlg = new CreateModDialog( ManagerFrame.this, modsDir );
createModDlg.addWindowListener( nerfListener );
//configDlg.setSize( 300, 400 );
createModDlg.setLocationRelativeTo( null );
createModDlg.setVisible( true );
}
else if ( source == sandboxMenuItem ) {
setStatusText( "" );
File datsDir = new File( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH ) );

View file

@ -0,0 +1,162 @@
package net.vhati.modmanager.xml;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import org.jdom2.CDATA;
import org.jdom2.Comment;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Text;
import org.jdom2.output.Format;
import org.jdom2.output.LineSeparator;
import org.jdom2.output.XMLOutputter;
import net.vhati.modmanager.core.EOLWriter;
public class JDOMModMetadataWriter {
/**
* Writes boilerplate metadata for a mod.
*
* @param outFile "{modDir}/mod-appendix/metadata.xml"
* @param modTitle
* @param modURL
* @param modAuthor
* @param modVersion
* @param modDesc
*/
public static void writeMetadata( File outFile, String modTitle, String modURL, String modAuthor, String modVersion, String modDesc ) throws IOException {
StringBuilder buf = new StringBuilder();
Element rootNode = new Element( "metadata" );
Document doc = new Document( rootNode );
rootNode.addContent( new Text( "\n" ) );
rootNode.addContent( new Text( "\t" ) );
buf.setLength( 0 );
buf.append( "\n" );
buf.append( "\t\tCDATA tags mean no need to escape special characters.\n" );
buf.append( "\t\tDon't worry about spaces at the start/end. That gets trimmed.\n" );
buf.append( "\t" );
rootNode.addContent( new Comment( buf.toString() ) );
rootNode.addContent( new Text( "\n\n\n" ) );
// title.
rootNode.addContent( new Text( "\t" ) );
rootNode.addContent( new Comment( String.format( " %s ", "The title of this mod." ) ) );
rootNode.addContent( new Text( "\n" ) );
rootNode.addContent( new Text( "\t" ) );
Element titleNode = new Element( "title" );
titleNode.setContent( new CDATA( String.format( " %s ", modTitle ) ) );
rootNode.addContent( titleNode );
rootNode.addContent( new Text( "\n\n\n" ) );
// threadUrl.
rootNode.addContent( new Text( "\t" ) );
buf.setLength( 0 );
buf.append( "\n" );
buf.append( "\t\tThis mod's thread on subsetgames.com.\n" );
buf.append( "\t\tIf there's no thread yet, create one to announce your upcoming mod in the\n" );
buf.append( "\t\tforum. Then paste the url here.\n" );
buf.append( "\t" );
rootNode.addContent( new Comment( buf.toString() ) );
rootNode.addContent( new Text( "\n" ) );
rootNode.addContent( new Text( "\t" ) );
Element urlNode = new Element( "threadUrl" );
urlNode.setContent( new CDATA( String.format( " %s ", modURL ) ) );
rootNode.addContent( urlNode );
rootNode.addContent( new Text( "\n\n\n" ) );
// author.
rootNode.addContent( new Text( "\t" ) );
rootNode.addContent( new Comment( String.format( " %s ", "Your forum user name." ) ) );
rootNode.addContent( new Text( "\n" ) );
rootNode.addContent( new Text( "\t" ) );
Element authorNode = new Element( "author" );
authorNode.setContent( new CDATA( String.format( " %s ", modAuthor ) ) );
rootNode.addContent( authorNode );
rootNode.addContent( new Text( "\n\n\n" ) );
// version.
rootNode.addContent( new Text( "\t" ) );
buf.setLength( 0 );
buf.append( "\n" );
buf.append( "\t\tThe revision/variant of this release, preferably at least a number.\n" );
buf.append( "\t\tExamples:\n" );
buf.append( "\t\t\t0.3\n" );
buf.append( "\t\t\t2.1c Ships Only WIP\n" );
buf.append( "\t\t\t2.4.1 Hi-res Bkgs\n" );
buf.append( "\t\t\t1.0 for FTL 1.03.1\n" );
buf.append( "\t" );
rootNode.addContent( new Comment( buf.toString() ) );
rootNode.addContent( new Text( "\n" ) );
rootNode.addContent( new Text( "\t" ) );
Element versionNode = new Element( "version" );
versionNode.setContent( new CDATA( String.format( " %s ", modVersion ) ) );
rootNode.addContent( versionNode );
rootNode.addContent( new Text( "\n\n\n" ) );
// description.
rootNode.addContent( new Text( "\t" ) );
Element descNode = new Element( "description" );
descNode.addContent( new Text( "\n" ) );
descNode.addContent( new CDATA( String.format( "\n%s\n", modDesc ) ) );
descNode.addContent( new Text( "\n\t" ) );
rootNode.addContent( descNode );
rootNode.addContent( new Text( "\n\n" ) );
buf.setLength( 0 );
buf.append( "\n" );
buf.append( "\tSuggestions for the description...\n" );
buf.append( "\n" );
buf.append( "\tWrite a short paragraph about the mod's effect first (what style ship, how\n" );
buf.append( "\tdoes it affect gameplay). No need to introduce yourself.\n" );
buf.append( "\n" );
buf.append( "\tOptionally add a paragraph of background flavor.\n" );
buf.append( "\n" );
buf.append( "\tOptionally list important features.\n" );
buf.append( "\n" );
buf.append( "\tList any concerns about mod compatibility, preferred order, or requirements.\n" );
buf.append( "\n" );
buf.append( "\tMention \"Replaces the XYZ ship.\" if relevant.\n" );
buf.append( "\t\tKestrel-A, Stealth-A, Mantis-A,\n" );
buf.append( "\t\tEngi-A, Fed-A, Slug-A,\n" );
buf.append( "\t\tRock-A, Zoltan-A, Crystal-A\n" );
buf.append( "\n" );
buf.append( "\tAbove all, keep the description general, so you won't have to edit\n" );
buf.append( "\tthat again for each new version.\n" );
buf.append( "\t" );
rootNode.addContent( new Comment( buf.toString() ) );
rootNode.addContent( new Text( "\n" ) );
Format format = Format.getPrettyFormat();
format.setTextMode( Format.TextMode.PRESERVE );
format.setExpandEmptyElements( false );
format.setOmitDeclaration( false );
format.setIndent( "\t" );
format.setLineSeparator( LineSeparator.CRNL );
Writer writer = null;
try {
writer = new EOLWriter( new FileWriter( outFile ), "\r\n" );
XMLOutputter xmlOutput = new XMLOutputter();
xmlOutput.setFormat( format );
xmlOutput.output( doc, writer );
}
finally {
try {if ( writer != null ) writer.close();}
catch ( IOException e ) {}
}
}
}