/*
* [Quoter.java]
*
* Summary: Performs various cleanups and conversions on text.
*
* Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
*
* Licence: This software may be copied and used freely for any purpose but military.
* http://mindprod.com/contact/nonmil.html
*
* Requires: JDK 1.8+
*
* Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
*
* Version History:
* 1.0 1998-12-25 initial release.
* 1.1 1998-12-28 handle empty or null clipboard better.
* handle Latin1, Windows and IBM OEM character set encodings.
* 1.2 1999-09-10 add Java source code strings as target.
* 1.3 1999-09-11 allow plain, just strip control chars
* trim lead/trail spaces.
* in Applet, choice changes are now Live and
* trigger a convert action.
* 1.4 1999-09-12 UPPER, lower and Title case conversion,
* text column alignment
* Java source alignment.
* 1.5 1999-09-12 allow Applet command line parameter to simulate
* Applet mode when running as application.
* fix bug in translate table that was sometimes stripping \ns.
* 1.6 1999-09-13 clearer Choice descriptions.
* 1.7 1999-09-18 add collapse multiple spaces
* 1.8 1999-12-02 high chars -> \ u xxxx
* 1.9 2000-05-15 HTML converts runs of blank lines to
* 2.0 2000-08-02 Funduc Regex converters
* fixed bug in Java string generator for \ u x x x x
* 2.1 2001-02-02 make sure + always gets quoted in Funduc search strings
* reorganise the code around TextProcessor and Translator
* base classes.
* 2.2 2001-02-02 avoid surrounding results in quotes
* 2.3 2002-03-06 preserve line breaks in generated HTML
* now see and marking line breaks.
* It is easier to take them out than add them manually.
* 2.4 2002-06-20 strip HTML tags and entities
* 2.5 2002-08-08 convert to Java style search/replace regexes.
* 2.6 2003-06-06 fix bug, excess in to HTML output.
* 2.7 2004-05-30 cleverer way of handling newlines when stripping tags to more
* closely parallel what the way the text was rendered HTML.
* added an about box.
* handle tabs with \t in Java string literals
* 2.8 2004-06-01 handle tabs with \t in Java string literals
* 2.9 2004-06-01 no longer use \' in "...", just plain '
* 3.0 2004-06-20 strip HTML tags will not be fooled by tags similar to standard ones.
* 3.1 2005-07-30 use ANT, document encodings better.
* more entities, use standard Entities package.
* 3.2 2005-09-03 expand size of window slightly
* 3.3 2006-03-05 reformat with IntelliJ, add javadoc.
* Convert to JDK 1.5 and swing with enums.
* 3.4 2006-03-07 remove translate feature from screen.
* add char[] transform
* 3.5 2007-03-26 fixes a bug in StripEntities that was mishandling
* 
* 3.6 2007-04-06 tidy code. proper size when run as application.
* tidy code. Convert AlignJava to an enum-based
* finite state machine.
* 3.7 2007-04-28 new logo, PAD.
* 3.8 2007-05-02 add Vslick regex support
* 3.9 2007-12-16 add support for encode/decode URL
* 4.0 2008-01-29 add > as a reserve character in Java regex.
* 4.1 2008-02-09 remove =<> as Java regex search reserved chars
* 4.2 2008-04-07 add build to title, tidy code, correct spelling, convert Align to use enum.
* Application mode now displays raw and cooked. Add Paste button.
* 4.3 2008-08-06 add support for XML strip tags and entities, insert entities.
* 4.4 2009-02-24 add Java string quoting to Java search/replace regexes.
* 4.5 2009-02-26 add both Java string quoting and plain for Java search/regexes.
* 4.6 2009-11-11 add . to list of quoted chars in Java regex. add ToCSV.
* 4.7 2011-01-03 add span processor
* 4.8 2011-11-15 add flow by stripping newline chars
* 4.9 2011-11-17 add swap button, now signed
* 5.0 2011-11-18 regex span now does both includes and excludes and displays the range two ways.
* 5.1 2012-01-13 configurable look and feel
* 5.2 2012-12-17 convert Java String literals back to plain strings.
* 5.3 2014-07-01 now show three Regex variants at once.
* 5.4 2014-07-24 correct the three Regex variants and improve the spacing.
* 5.5 2014-09-13 add hex display of codepoints
*/
package com.mindprod.quoter;
import com.mindprod.common18.Build;
import com.mindprod.common18.CMPAboutJBox;
import com.mindprod.common18.ClipboardPoker;
import com.mindprod.common18.FontFactory;
import com.mindprod.common18.HybridJ;
import com.mindprod.common18.JEButton;
import com.mindprod.common18.Laf;
import com.mindprod.common18.VersionCheck;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import static com.mindprod.quoter.TargetOption.STRIP_NEWLINES;
import static com.mindprod.quoter.TrimOption.TRIM;
import static java.lang.System.*;
/**
* Performs various cleanups and conversions on text.
*
*
* @author Roedy Green, Canadian Mind Products
* @version 5.5 2014-09-13 add hex display of codepoints
* @noinspection FieldCanBeLocal
* @since 1998-12-25
*/
public final class Quoter extends JApplet implements ClipboardOwner
{
/**
* true if want extra debugging info.
*
* @noinspection WeakerAccess
*/
static final boolean DEBUGGING = false;
/**
* true can do copy/paste. Always now. Previously could not as unsigned Applets.
*/
private static final boolean canPaste = true;
/**
* height of Applet box in pixels. has no effect on application width. Does not include surrounding frame. Height
* to use in <applet tag.
*
* @noinspection UnusedDeclaration
*/
private static final int APPLET_HEIGHT = 528;
/**
* Width of Applet box in pixels, has no effect on application width. Height to use in <applet tag.
*
* @noinspection UnusedDeclaration
*/
private static final int APPLET_WIDTH = 636;
private static final int FIRST_COPYRIGHT_YEAR = 1998;
/**
* non-displaying copyright.
*
* @noinspection UnusedDeclaration
*/
private static final String EMBEDDED_COPYRIGHT =
"Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com";
private static final String RELEASE_DATE = "2014-09-13";
private static final String TITLE_STRING = "Quoter Amanuensis";
private static final String VERSION_STRING = "5.5";
private static final Color BACKGROUND_FOR_BOX = Color.WHITE;
private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 )/* dark green */;
private static final Color FOREGROUND_FOR_TEXT = Color.BLACK;
/**
* for titles
*/
private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c );
/**
* for for titles and About buttons
*/
private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 );
/**
* for for title second line
*/
private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 );
/**
* clear everything button.
*/
private JButton clearButton;
/**
* convert text button.
*/
private JButton convertButton;
/**
* paste button.
*/
private JButton pasteButton;
/**
* swap input and output.
*/
private JButton swapButton;
/**
* Control to select what convert text into.
*/
private JComboBox targetChoice;
/**
* control to select what sort of blank trimming is wanted.
*/
private JComboBox trimChoice;
/**
* instructions on what to d.
*/
private JLabel instructions;
/**
* title of Applet.
*/
private JLabel title;
/**
* title, second line
*/
private JLabel title2;
/**
* converted text.
*/
private JTextArea cookedTextArea;
/**
* text input to be converted.
*/
private JTextArea rawTextArea;
/**
* what to convert the text into.
*/
private TargetOption targetOption;
/**
* which post processing to do, align, case, blank collapse.
*/
private TextProcessor postprocessor;
/**
* which preprocessing to use, usually just trim.
*/
private TextProcessor preprocessor;
/**
* which translation to use, char by char to a string.
*/
private Translator translator;
/**
* how to trim the text.
*/
private TrimOption trimOption;
/**
* allocate all Components
*/
private void buildComponents()
{
title = new JLabel( TITLE_STRING + " " + VERSION_STRING );
title.setFont( FONT_FOR_TITLE );
title.setForeground( FOREGROUND_FOR_TITLE );
title2 = new JLabel(
"released:" +
RELEASE_DATE +
" build:" +
Build.BUILD_NUMBER
);
title2.setFont( FONT_FOR_TITLE2 );
title2.setForeground( FOREGROUND_FOR_TITLE );
rawTextArea = new JTextArea( "", 3, 50 );
rawTextArea.setEditable( true );
rawTextArea.setForeground( FOREGROUND_FOR_TEXT );
rawTextArea.setBackground( BACKGROUND_FOR_BOX );
cookedTextArea = new JTextArea( "", 3, 50 );
cookedTextArea.setEditable( false );
cookedTextArea.setForeground( FOREGROUND_FOR_TEXT );
cookedTextArea.setBackground( BACKGROUND_FOR_BOX );
decideFonts();
targetChoice = new JComboBox<>();
// add legal choices to choice box in same order as enums.
// We add the enums themselves, not the descriptive Strings.
for ( TargetOption possibility : TargetOption.values() )
{
targetChoice.addItem( possibility );
}
targetChoice.setSelectedItem( targetOption ); // set initial value
targetChoice.setMaximumRowCount( TargetOption.values().length );
trimChoice = new JComboBox<>();
for ( TrimOption possibility : TrimOption.values() )
{
trimChoice.addItem( possibility );
}
trimChoice.setSelectedItem( trimOption );
trimChoice.setMaximumRowCount( TrimOption.values().length );
add( trimChoice );
clearButton = new JEButton( "Clear" );
clearButton.setToolTipText( "Clear everything" );
swapButton = new JEButton( "Swap" );
swapButton.setToolTipText(
"Swap the lower and upper panel contents so you can apply another transform to the output." );
convertButton = new JEButton( "Convert" );
convertButton.setToolTipText( "Convert text in upper panel" );
if ( canPaste )
{
// can't paste the clipboard in an unsigned Applet
pasteButton = new JEButton( "Convert Clipboard" );
pasteButton.setToolTipText( "Paste and convert clipboard" );
}
instructions = new JLabel( "", JLabel.CENTER );
instructions.setFont( FontFactory.build( "Dialog", Font.PLAIN, 11 ) );
instructions.setBackground( BACKGROUND_FOR_BOX );
instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS );
}
/**
* build a menu with Look & Feel and About across the top
*/
private void buildMenu()
{
// turn on anti-alias
System.setProperty( "swing.aatext", "true" );
final JMenuBar menubar = new JMenuBar();
setJMenuBar( menubar );
final JMenu lafMenu = Laf.buildLookAndFeelMenu();
if ( lafMenu != null )
{
menubar.add( lafMenu );
}
final JMenu menuHelp = new JMenu( "Help" );
menubar.add( menuHelp );
final JMenuItem aboutItem = new JMenuItem( "About" );
menuHelp.add( aboutItem );
aboutItem.addActionListener( new ActionListener()
{
public void actionPerformed( ActionEvent e )
{
// open aboutbox frame
new CMPAboutJBox( TITLE_STRING,
VERSION_STRING,
"Does various text manipulations.",
"In application mode, the clipboard is automatic.",
"freeware",
RELEASE_DATE,
FIRST_COPYRIGHT_YEAR,
"Roedy Green",
"QUOTER",
"1.8" );
}
} );
}
/**
* action when Convert button pressed This is a method of quoter, not TheListener.
*
* @noinspection ConstantConditions
*/
private void convertAction()
{
/* get user's selections and decide work to be done */
getSelections();
/**
* The original format of the clipboard before conversion. Lines in the
* clipboard are separated with \n characters. There may or may not be a
* final \n. I have only tested this code under NT. I am not sure if
* this is universal.
*/
// as Applet, must rely on manual cut paste to raw and cooked
// TextAreas.
final String raw = rawTextArea.getText();
if ( DEBUGGING )
{
// dump the raw input to view.
final char[] ca = raw.toCharArray();
for ( char aCa : ca )
{
out.println( aCa + " " + ( int ) aCa );
} // end for
} // end if debugging
/**
* The converted form of the clipboard after conversion.
*/
String cooked = raw;
// process the contents of the clipboard
// This the guts of the program
if ( preprocessor != null )
{
cooked = preprocessor.process( cooked );
}
if ( translator != null )
{
cooked = translator.process( cooked );
}
if ( postprocessor != null )
{
cooked = postprocessor.process( cooked );
}
cookedTextArea.setText( cooked );
if ( canPaste )
{
// can't poke the clipboard in an unsigned Applet
ClipboardPoker.setClip( cooked, this );
} // end else
} // end ConvertAction
/**
* choose fonts.
*/
private void decideFonts()
{
final Font font = targetOption.getFont();
rawTextArea.setFont( font );
cookedTextArea.setFont( font );
} // end decideFonts
/**
* decide on the instructions to the user.
*/
private void decideInstructions()
{
instructions
.setText( canPaste ? targetOption.getApplicationInstructions()
: targetOption.getAppletInstructions() );
}
/**
* decide post processing needed.
*/
private void decidePostprocessor()
{
postprocessor = targetOption.getProcessor();
}
/**
* decide trim preprocessing needed.
*/
private void decidePreprocessor()
{
preprocessor = trimOption.getProcessor();
}
/**
* decide basic translation.
*/
private void decideTranslator()
{
translator = targetOption.getTranslator();
}
/**
* Get the state of the various choice boxes and set up corresponding internal variables to plan the translations
* and processing we will do. This controls the main application logic.
*/
private void getSelections()
{
// get contents of multiple choice fields on screen
targetOption = ( TargetOption ) targetChoice.getSelectedItem();
trimOption = ( TrimOption ) trimChoice.getSelectedItem();
decideInstructions();
decidePreprocessor();
decideTranslator();
decidePostprocessor();
decideFonts();
} // end getSelections
/**
* register listeners
*/
private void hookListeners()
{
// REGISTER LISTENER
final TheListener theListener = new TheListener();
clearButton.addActionListener( theListener );
swapButton.addActionListener( theListener );
convertButton.addActionListener( theListener );
if ( canPaste )
{
pasteButton.addActionListener( theListener );
}
targetChoice.addActionListener( theListener );
trimChoice.addActionListener( theListener );
}
/**
* layout the components
*
* @param contentPane where to place the Components
*/
private void layoutComponents( Container contentPane )
{
// Applet Gridbag:
// 0------1---2---- 3----
// title------------title2- 0
// --------raw------------ 1
// -------cooked---------- 2
// targetChoice trimchoice 3
// clear swap paste convert 4
// -----instructions------- 5
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( title,
new GridBagConstraints( 0,
0,
2,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.NONE,
new Insets( 10, 10, 5, 10 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( title2,
new GridBagConstraints( 2,
0,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.NONE,
new Insets( 10, 5, 5, 10 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
// put inside a scroller
contentPane.add( new JScrollPane( rawTextArea,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ),
new GridBagConstraints( 0,
1,
4,
1,
0.5,
0.5,
GridBagConstraints.CENTER,
GridBagConstraints.BOTH,
new Insets( 0, 10, 10, 10 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( new JScrollPane( cookedTextArea,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ),
new GridBagConstraints( 0,
2,
4,
1,
0.5,
1.0,
GridBagConstraints.CENTER,
GridBagConstraints.BOTH,
new Insets( 0, 10, 10, 10 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( targetChoice,
new GridBagConstraints( 0,
3,
2,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.BOTH,
new Insets( 0, 10, 5, 5 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( trimChoice,
new GridBagConstraints( 2,
3,
2,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.BOTH,
new Insets( 0, 5, 5, 10 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( clearButton,
new GridBagConstraints( 0,
4,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.NONE,
new Insets( 5, 10, 5, 5 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( swapButton,
new GridBagConstraints( 1,
4,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.NONE,
new Insets( 5, 10, 5, 5 ),
0,
0 )
);
if ( canPaste )
{
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( pasteButton,
new GridBagConstraints( 2,
4,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.NONE,
new Insets( 5, 5, 5, 5 ),
0,
0 )
);
}
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( convertButton,
new GridBagConstraints( 3,
4,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.NONE,
new Insets( 5, 5, 5, 10 ),
0,
0 )
);
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( instructions,
new GridBagConstraints( 0,
5,
4,
1,
0.0,
0.0,
GridBagConstraints.CENTER,
GridBagConstraints.NONE,
new Insets( 0, 10, 10, 10 ),
0,
0 )
);
}
/**
* Allow this Applet to run as as application as well.
*
* @param args command line arguments ignored.
*/
public static void main( String args[] )
{
HybridJ.fireup( new Quoter(),
TITLE_STRING + " " + VERSION_STRING,
APPLET_WIDTH,
APPLET_HEIGHT );
} // end main
/**
* Called by the browser or Applet viewer to inform
* this Applet that it is being reclaimed and that it should destroy
* any resources that it has allocated.
*/
@Override
public void destroy()
{
clearButton = null;
convertButton = null;
cookedTextArea = null;
instructions = null;
pasteButton = null;
postprocessor = null;
preprocessor = null;
rawTextArea = null;
swapButton = null;
targetChoice = null;
targetOption = null;
title2 = null;
title = null;
translator = null;
trimChoice = null;
trimOption = null;
}
/**
* Called by the browser or Applet viewer to inform
* this Applet that it has been loaded into the system.
*/
@Override
public void init()
{
if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) )
{
// effectively abort
return;
}
buildMenu();
Container contentPane = this.getContentPane();
contentPane.setLayout( new GridBagLayout() );
contentPane.setBackground( BACKGROUND_FOR_BOX );
targetOption = STRIP_NEWLINES;
trimOption = TRIM;
buildComponents();
getSelections();
layoutComponents( contentPane );
hookListeners();
this.validate();
this.setVisible( true );
} // end init
/**
* If we put data in the Clipboard, and somebody else changes the clipboard, we lose ownership and are notified
* here. Satisfies the requirement of this class to implement datatransfer.ClipboardOwner.
*
* @param clipboard clipboard handle.
* @param contents contents of clipboard.
*/
public final void lostOwnership( Clipboard clipboard,
Transferable contents )
{
// we don't care
}
/**
* Inner class to handle button events.
*/
@SuppressWarnings( { "WeakerAccess" } )
final class TheListener implements ActionListener
{
public void actionPerformed( ActionEvent event )
{
final Object source = event.getSource();
if ( source == convertButton || source == targetChoice || source == trimChoice )
{
convertAction();
}
else if ( source == pasteButton )
{
// automatically paste clipboard into upper window, then convert.
rawTextArea.setText( ClipboardPoker.getClip( Quoter.this ) );
convertAction();
}
else if ( source == clearButton )
{
rawTextArea.setText( null );
cookedTextArea.setText( null );
}
else if ( source == swapButton )
{
final String oldRaw = rawTextArea.getText();
final String oldCooked = cookedTextArea.getText();
rawTextArea.setText( oldCooked );
cookedTextArea.setText( oldRaw );
}
} // end actionPerformed
} // end TheListener inner Class
}