/* * [MASMBalancer.java] * * Summary: Helps you find unbalanced proc/endp segment/ends if/endif in MASM. * * Copyright: (c) 2011-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 2011-01-03 initial version. * 1.1 2011-01-06 add sound effects, handle clipboard without final nl. * 1.2 2011-02-07 allow individual balancing, also depth bars. * 1.3 2012-03-23 support if1, if2, ifdef, ifndef, ifb, ifnb, export button */ package com.mindprod.masmbalancer; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.ClipboardPoker; import com.mindprod.common18.EIO; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.JEButton; import com.mindprod.common18.Laf; import com.mindprod.common18.Misc; import com.mindprod.common18.Play; import com.mindprod.common18.VersionCheck; import com.mindprod.fastcat.FastCat; import com.mindprod.hunkio.HunkIO; import javax.swing.DefaultComboBoxModel; import javax.swing.JApplet; import javax.swing.JComboBox; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; 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 java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.File; import java.io.IOException; import static java.lang.System.*; /** * Helps you find unbalanced proc/endp segment/ends if/endif in MASM. *

* This is both an Applet and a standalone application. * * @author Roedy Green, Canadian Mind Products * @version 1.3 2012-03-23 support if1, if2, ifdef, ifndef, ifb, ifnb, export button * @since 2011-01-03 */ public final class MASMBalancer extends JApplet implements ClipboardOwner, Runnable { // togo: Masm JPrep // togo: why does need final Cr? /** * height of Applet in pixels. Does not include bar at top, but does include menu bar. */ private static final int APPLET_HEIGHT = 900; /** * Width of Applet in pixels. */ private static final int APPLET_WIDTH = 758; private static final int FIRST_COPYRIGHT_YEAR = 2011; /** * undisplayed copyright notice * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2011-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * date this version released. */ private static final String RELEASE_DATE = "2012-03-23"; /** * Title of the Applet */ private static final String TITLE_STRING = "MASM Balancer"; /** * Version of the Applet */ private static final String VERSION_STRING = "1.3"; /** * background colour, pale green to match website */ private static final Color BACKGROUND_FOR_BODY = Build.BACKGROUND_FOR_BLENDING; /** * background where for data entry where user enters a value. */ private static final Color BACKGROUND_FOR_EDITABLE = Color.WHITE; /** * data entry text field colour */ private static final Color FOREGROUND_FOR_EDITABLE = Color.BLACK; private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 ); private static final Color FOREGROUND_FOR_PROGRESS = new Color( 0x000080 ); /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * title font */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 15 ); /** * for for title second line */ private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); private static final Font INSTRUCTIONS_FONT = FontFactory.build( "Dialog", Font.PLAIN, 13 ); private static final Font PROGRESS_FONT = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * which balancer the user selected. */ private Balancer balancer; /** * contentPane for the whole Applet */ private Container contentPane; /** * which syntactic elements to balance. */ private JComboBox whatToBalance; /** * invoke colourising. */ private JEButton export; /** * invoke colourising. */ private JEButton paste; /** * display instructions for use */ private JEditorPane instructions; /** * where we display the code with unbalanced delimiter */ private JEditorPane jep; /** * let user know progress of colorising */ private JLabel progress; /** * name of app */ private JLabel title; /** * build and version */ private JLabel title2; /** * scrollpane to hold the JEditorPane */ private JScrollPane jepScrollPane; /** * html-css formatted program text */ private String formattedProgramText; /** * clipboard contents before we colourise it */ private String rawClipboard; /** * build all the Swing components. */ private void buildComponents() { contentPane.setBackground( BACKGROUND_FOR_BODY ); 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 ); whatToBalance = new JComboBox<>(); // turn off the write-in feature. whatToBalance.setEditable( false ); whatToBalance.setModel( new DefaultComboBoxModel<>( Balancer.values() ) ); whatToBalance.setSelectedItem( Balancer.ALL ); balancer = Balancer.ALL; jep = new JEditorPane(); jep.setEditable( false ); jep.setContentType( "text/html" ); jep.setForeground( FOREGROUND_FOR_EDITABLE ); jep.setBackground( BACKGROUND_FOR_EDITABLE ); jep.setMargin( new Insets( 5, 5, 5, 5 ) ); jepScrollPane = new JScrollPane( jep, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); final FastCat sb = new FastCat( 6 ); // cannot use any css, HTML 3.2 only sb.append( "" ); sb.append( "" ); sb.append( "

\n" ); sb.append( "" ); final String instructionsHTML = sb.toString(); instructions = new JEditorPane(); instructions.setContentType( "text/html" ); instructions.setEditable( false ); instructions.setMargin( new Insets( 5, 5, 5, 5 ) ); instructions.setFont( INSTRUCTIONS_FONT ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); try { instructions.setText( instructionsHTML ); } catch ( Exception e ) { Play.play( MASMBalancer.class, "sound/echo.au" ); err.println( "Because of Sun bug, unable to display instructions, please exit browser and restart" ); e.printStackTrace( err ); err.println( instructionsHTML ); } paste = new JEButton( "Paste" ); progress = new JLabel(); progress.setFont( PROGRESS_FONT ); progress.setForeground( FOREGROUND_FOR_PROGRESS ); export = new JEButton( "Export" ); } /** * 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 about frame new CMPAboutJBox( Misc.getParentFrame( MASMBalancer.this ), TITLE_STRING, VERSION_STRING, "Helps you balance mismatched proc/endp segment/ends if/endif in MASM source code.", "", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "MASMBALANCER", "1.8" ); } } ); } /** * colourise backgrounds according to the nesting depth */ private void colouriseAndDisplayClipboard() { progress.setText( "Collecting the clipboard. Please be patient..." ); // contents of the clipboard before processing. rawClipboard = ClipboardPoker.getClip( this ); if ( rawClipboard != null && rawClipboard.length() > 0 ) { progress.setText( "Colourising the clipboard. Please be patient..." ); Play.play( MASMBalancer.class, "sound/cork.au" ); // fire up run on separate thread to parse the clipboard and render it // needs cast even though whatToBalance is generic balancer = ( Balancer ) whatToBalance.getSelectedItem(); new Thread( this ).start(); // start up run on a new thread. } else { progress.setText( "empty clipboard..." ); Play.play( MASMBalancer.class, "sound/echo.au" ); err.println( "empty clipboard" ); } } /** * hook up the listeners */ private void hookListeners() { paste.addActionListener( new ActionListener() { public void actionPerformed( final ActionEvent e ) { colouriseAndDisplayClipboard(); } } ); whatToBalance.addItemListener( new ItemListener() { /** * Notice any change to one of the list box selectors * @param e details of just what the user clicked. */ public void itemStateChanged( ItemEvent e ) { if ( e.getStateChange() == ItemEvent.SELECTED ) { recolouriseAndDisplayClipboard(); } } } ); // // what to do when user clicks the export button. export.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { final JFileChooser fc = new JFileChooser(); // No filter. No suggested starting point. switch ( fc.showOpenDialog( MASMBalancer.this ) ) { case JFileChooser.APPROVE_OPTION: final File file = fc.getSelectedFile(); fc.setFileSelectionMode( JFileChooser.FILES_ONLY ); try { HunkIO.writeEntireFile( file, formattedProgramText, HunkIO.UTF8 ); progress.setText( "Formatted program text exported as HTML to " + EIO.getCanOrAbsPath ( file ) ); } catch ( IOException ioe ) { progress.setText( "Unable to export formatted program" ); } break; case JFileChooser.CANCEL_OPTION: case JFileChooser.ERROR_OPTION: break; default: } } } ); } /** * layout fields in GridBag */ private void layoutComponents() { contentPane.setLayout( new GridBagLayout() ); // _____ 0_____________________________1_____2___---- // 0 Title------------------- ------title2---------- 0 // 1 whattoBalance ---------- ------------- ------- 1 // 2 jep -------------------- -------------- ------- 2 // 3 instructions---------------------------export-- 3 // 4 -------------------------------------- paste ---4 // 5 progress--------------------------------------- 5 // // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 5 ), // top, left, bottom, right 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title2, new GridBagConstraints( 1, 0, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 5, 5, 10 ), // top, left, bottom, right 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( whatToBalance, new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 5, 10, 5, 5 ), // top, left, bottom, right 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( jepScrollPane, new GridBagConstraints( 0, 2, 3, 1, 100.0, 100.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 10 ), // top, left, bottom, right 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions, new GridBagConstraints( 0, 3, 2, 2, 90.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 5 ), // top, left, bottom, right 40, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( export, new GridBagConstraints( 2, 3, 1, 1, 10.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 5, 5, 10 ), // top, left, bottom, right 10, 10 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( paste, new GridBagConstraints( 2, 4, 1, 1, 10.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 5, 5, 10 ), // top, left, bottom, right 10, 10 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( progress, new GridBagConstraints( 0, 5, 3, 1, 100.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 5, 10, 10, 5 ), // top, left, bottom, right 40, 0 ) ); } /** * recolourise the clipboard previously extracted with different balancer */ private void recolouriseAndDisplayClipboard() { final Balancer oldBalancer = balancer; balancer = ( Balancer ) whatToBalance.getSelectedItem(); if ( balancer != oldBalancer && rawClipboard != null && rawClipboard.length() > 0 ) { progress.setText( "Colourising the clipboard. Please be patient..." ); new Thread( this ).start(); // start up run on a new thread to } } /** * 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 MASMBalancer(), 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() { balancer = null; contentPane = null; instructions = null; jep = null; jepScrollPane = null; paste = null; progress = null; rawClipboard = null; title = null; title2 = null; whatToBalance = null; } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { contentPane = getContentPane(); if ( !VersionCheck.isJavaVersionOK( 1, 7, 0, contentPane ) ) { // abort stop(); destroy(); } buildMenu(); // also initial L&F buildComponents(); layoutComponents(); hookListeners(); this.validate(); this.setVisible( true ); } /** * needed to let us use the clipboard */ public void lostOwnership( Clipboard clipboard, Transferable contents ) { /* ignore */ } /** * on a separate thread, prepare contents of clipboard for display. * On entry rawClipboard contains clipboard. */ public void run() { formattedProgramText = MASMState.colourise( rawClipboard, balancer ); // now we have prepared the html, render the results SwingUtilities.invokeLater( new Runnable() { public void run() { try { jep.setText( formattedProgramText ); Play.play( MASMBalancer.class, "sound/start.au" ); progress.setText( "" ); } catch ( Exception e ) { Play.play( MASMBalancer.class, "sound/echo.au" ); err.println( "Unable to render that clipboard because of " + e.getMessage() ); err.println( "cooked: [" + formattedProgramText + "]" ); jep.setText( "Unable to render that clipboard" ); } } } ); } }