import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.*;
import sos.marker.*;
import java.util.List;
import javax.swing.event.DocumentListener;
public class MarkerBarDemo extends JFrame
{
static final Map<RenderingHints.Key,Object> hints = new HashMap<RenderingHints.Key,Object>();
static {
hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
}
private static final Color HIGHLIGHT_COLOR = Color.ORANGE;
private MarkerBarDemo() throws Exception
{
final Map<String,Border> borders = new LinkedHashMap<String,Border>();
borders.put( "None", null );
borders.put( "Etched Raised", BorderFactory.createEtchedBorder( EtchedBorder.RAISED ));
borders.put( "Etched Lowered", BorderFactory.createEtchedBorder( EtchedBorder.LOWERED ));
borders.put( "Line", BorderFactory.createLineBorder( Color.BLACK ));
borders.put( "Top/Bottom", BorderFactory.createMatteBorder( 1, 0, 1, 0, Color.BLACK ));
borders.put( "Sides", BorderFactory.createMatteBorder( 0, 1, 0, 1, Color.BLACK ));
Object[] bordersArray = borders.keySet().toArray();
final Map<String,MarkerRenderer> renderers = new LinkedHashMap<String,MarkerRenderer>();
MarkerRenderer markerRenderer = new DefaultMarkerRenderer();
renderers.put( "Rectangle", markerRenderer );
renderers.put( "Circle", new CircleMarkerRenderer() );
Object[] renderersArray = renderers.keySet().toArray();
final Map<String,Color> colors = new LinkedHashMap<String,Color>();
colors.put( "None", null );
colors.put( "Black", Color.BLACK );
colors.put( "Blue", Color.BLUE );
colors.put( "Cyan", Color.CYAN );
colors.put( "Gray", Color.GRAY );
colors.put( "Green", Color.GREEN );
colors.put( "Light Gray", Color.LIGHT_GRAY );
colors.put( "Magenta", Color.MAGENTA );
colors.put( "Orange", Color.ORANGE );
colors.put( "Pink", Color.PINK );
colors.put( "Red", Color.RED );
colors.put( "Yellow", Color.YELLOW );
Object[] colorsArray = colors.keySet().toArray();
List<Object> digitsPlusBlank = new ArrayList<Object>();
digitsPlusBlank.add( "" );
for( int i = 1; i <= 50; i++ )
digitsPlusBlank.add( i );
JPanel searchPane = new JPanel();
BoxLayout searchPaneLayout = new BoxLayout( searchPane, BoxLayout.LINE_AXIS );
searchPane.setLayout( searchPaneLayout );
searchPane.setBorder( BorderFactory.createEmptyBorder( 10, 4, 10, 4 ));
final JTextField searchField = new JTextField();
Dimension preferredSize = searchField.getPreferredSize();
Dimension inputSize = new Dimension( 120, (int)preferredSize.getHeight() );
searchField.setPreferredSize( inputSize );
searchField.setMinimumSize( inputSize );
searchField.setMaximumSize( inputSize );
JLabel searchLabel = new JLabel( "Search:" );
searchLabel.setLabelFor( searchField );
searchPane.add( Box.createHorizontalGlue() );
searchPane.add( searchLabel );
searchPane.add( searchField );
final JTextPane textPane = new JTextPane();
Highlighter highlighter = new DefaultHighlighter();
final Highlighter.HighlightPainter highlightPainter = new DefaultHighlighter.DefaultHighlightPainter( HIGHLIGHT_COLOR );
textPane.setHighlighter( highlighter );
StyleContext styleContext = new StyleContext();
Style defaultAttributes = styleContext.getStyle( StyleContext.DEFAULT_STYLE );
Style bodyAttributes = styleContext.addStyle( "Body", defaultAttributes );
StyleConstants.setFontSize( bodyAttributes, 12 );
StyleConstants.setSpaceBelow( bodyAttributes, 12.0f );
Style titleAttributes = styleContext.addStyle( "Title", defaultAttributes );
StyleConstants.setFontSize( titleAttributes, 18 );
StyleConstants.setAlignment( titleAttributes, StyleConstants.ALIGN_CENTER );
StyleConstants.setBold( titleAttributes, true );
StyleConstants.setSpaceBelow( titleAttributes, 12.0f );
Style headerAttributes = styleContext.addStyle( "Header", defaultAttributes );
StyleConstants.setFontSize( headerAttributes, 14 );
StyleConstants.setBold( headerAttributes, true );
DefaultStyledDocument doc = new DefaultStyledDocument( styleContext );
doc.insertString( 0,
"Marker Bar Demo\n" +
"Summary\n" +
"This sample application uses a vertical marker bar next to a scrolling JTextPane " +
"to show search results. As you type into the search field above, search results " +
"are marked along the scrollbar to indicate where the matched characters fall " +
"within this document. The markers are drawn either as a rectangle or as a circle, " +
"depending on the option chosen in the side panel. The rectangle renderer is the " +
"default marker renderer, while the circle renderer is a custom renderer. Any " +
"AWT or Swing component can be used to draw the markers, making it easy to define " +
"your own marker renderer.\n" +
"The side panel also contains options to change the colors, borders, and preferred " +
"sizes of the marker bar and markers. If the marker preferred size is set " +
"to Integer.MAX_INTEGER by " +
"Integer.MAX_INTEGER (blank in this demo), then the marker bar sizes the renderer to the full " +
"width and height given to the marker. Otherwise, the marker bar sizes the renderer according to its " +
"preferred and minimum sizes. If the final size is less than the entire space represented " +
"by the marker, then the marker bar uses the renderer's X and Y alignments to position " +
"the mark horizontally and vertically.\n" +
"When you hover the mouser cursor over a marker, a tool tip displays, " +
"identifying the marker, and the cursor changes icons, indicating that the " +
"marker is clickable. In this demo, clicking on a marker merely scrolls the text pane " +
"so that the search result represented by the clicked marker is visible. You can easily " +
"perform another action by installing a different action listener.\n" +
"Other Side of Software Products\n" +
"While visiting sideofsoftware.com, please check out our other libraries. " +
"The Print Preview Library makes it easy to add a print preview screen to your " +
"application. The Report Library allows you to add stylish, editable tables to " +
"your application. The Wizard Library allows you to easily create step-by-step " +
"instructions within a dialog or window. The Dated Collections Library is a " +
"powerful utility library that efficiently adds dates or timestamps to elements within " +
"collections. Last, the Persistence Library turns your ordinary domain objects " +
"into persistent database objects without requiring you to specify any relational " +
"database mappings.",
defaultAttributes );
doc.setParagraphAttributes( 0, 16, titleAttributes, false );
doc.setParagraphAttributes( 16, 8, headerAttributes, false );
doc.setParagraphAttributes( 24, 1552, bodyAttributes, false );
doc.setParagraphAttributes( 1576, 32, headerAttributes, false );
doc.setParagraphAttributes( 1608, 100, bodyAttributes, false );
textPane.setDocument( doc );
JScrollPane scrollPane = new JScrollPane(textPane);
final JPanel panel = new JPanel();
final DefaultListModel model = new DefaultListModel();
final JMarkerBar markerBar = new JMarkerBar( model );
markerBar.setMarkerRenderer( markerRenderer );
JPanel optionsPane = new JPanel();
optionsPane.setLayout( new BoxLayout( optionsPane, BoxLayout.PAGE_AXIS ));
optionsPane.setBorder( BorderFactory.createEmptyBorder( 0, 20, 0, 20 ));
SpinnerModel markerBarWidthSpinnerModel = new SpinnerNumberModel( 12, 0, 50, 1 );
final JSpinner markerBarWidthSpinner = new JSpinner( markerBarWidthSpinnerModel );
markerBarWidthSpinner.addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e )
{
JSpinner spinner = (JSpinner)e.getSource();
Number value = (Number)spinner.getValue();
int v = value.intValue();
if( v == 0 )
markerBar.setPreferredSize( null );
else
{
Dimension preferredSize = markerBar.getPreferredSize();
Dimension newPreferredSize = new Dimension( v, preferredSize.height );
markerBar.setPreferredSize( newPreferredSize );
}
panel.revalidate();
panel.repaint();
}
} );
JLabel markerBarWidthLabel = new JLabel( "Width: " );
markerBarWidthLabel.setLabelFor( markerBarWidthSpinner );
final JComboBox markerBarBorderComboBox = new JComboBox( bordersArray );
markerBarBorderComboBox.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{
String borderName = (String)markerBarBorderComboBox.getSelectedItem();
Border border = borders.get( borderName );
markerBar.setBorder( border );
}
} );
JLabel markerBarBorderLabel = new JLabel( "Border: " );
markerBarBorderLabel.setLabelFor( markerBarBorderComboBox );
final JComboBox markerBarBackgroundComboBox = new JComboBox( colorsArray );
markerBarBackgroundComboBox.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{
String colorName = (String)markerBarBackgroundComboBox.getSelectedItem();
Color color = colors.get( colorName );
markerBar.setBackground( color );
}
} );
JLabel markerBarBackgroundLabel = new JLabel( "Background: " );
markerBarBackgroundLabel.setLabelFor( markerBarBackgroundComboBox );
final JComboBox markerForegroundComboBox = new JComboBox( colorsArray );
markerForegroundComboBox.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{
String colorName = (String)markerForegroundComboBox.getSelectedItem();
Color color = colors.get( colorName );
if( color == null )
color = new JMarkerBar().getForeground();
markerBar.setForeground( color );
}
} );
JLabel markerForegroundLabel = new JLabel( "Color: " );
markerForegroundLabel.setLabelFor( markerForegroundComboBox );
SpinnerModel markerHeightSpinnerModel = new SpinnerListModel( digitsPlusBlank );
final JSpinner markerHeightSpinner = new JSpinner( markerHeightSpinnerModel );
setSpinnerTextAlignment( markerHeightSpinner );
markerHeightSpinner.addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e )
{
JSpinner spinner = (JSpinner)e.getSource();
Object value = spinner.getValue();
int v;
if( "".equals( value ))
v = Integer.MAX_VALUE;
else
v = ((Number)value).intValue();
for( MarkerRenderer markerRenderer : renderers.values() )
{
JComponent component = (JComponent)markerRenderer;
Dimension preferredSize = component.getPreferredSize();
Dimension newPreferredSize = new Dimension( preferredSize.width, v );
component.setPreferredSize( newPreferredSize );
}
markerBar.revalidate();
markerBar.repaint();
}
} );
JLabel markerHeightLabel = new JLabel( "Height: " );
markerHeightLabel.setLabelFor( markerHeightSpinner );
SpinnerModel markerWidthSpinnerModel = new SpinnerListModel( digitsPlusBlank );
final JSpinner markerWidthSpinner = new JSpinner( markerWidthSpinnerModel );
setSpinnerTextAlignment( markerWidthSpinner );
markerWidthSpinner.addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e )
{
JSpinner spinner = (JSpinner)e.getSource();
Object value = spinner.getValue();
int v;
if( "".equals( value ))
v = Integer.MAX_VALUE;
else
v = ((Number)value).intValue();
for( MarkerRenderer markerRenderer : renderers.values() )
{
JComponent component = (JComponent)markerRenderer;
Dimension preferredSize = component.getPreferredSize();
Dimension newPreferredSize = new Dimension( v, preferredSize.height );
component.setPreferredSize( newPreferredSize );
}
markerBar.revalidate();
markerBar.repaint();
}
} );
JLabel markerWidthLabel = new JLabel( "Width: " );
markerWidthLabel.setLabelFor( markerWidthSpinner );
SpinnerModel markerAlignmentXSpinnerModel = new SpinnerNumberModel( 0.5, 0.0, 1.0, 0.1 );
final JSpinner markerAlignmentXSpinner = new JSpinner( markerAlignmentXSpinnerModel );
markerAlignmentXSpinner.addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e )
{
JSpinner spinner = (JSpinner)e.getSource();
Number value = (Number)spinner.getValue();
float v = value.floatValue();
for( MarkerRenderer markerRenderer : renderers.values() )
{
JComponent component = (JComponent)markerRenderer;
component.setAlignmentX( v );
}
markerBar.revalidate();
markerBar.repaint();
}
} );
JLabel markerAlignmentXLabel = new JLabel( "Alignment X: " );
markerAlignmentXLabel.setLabelFor( markerAlignmentXSpinner );
SpinnerModel markerAlignmentYSpinnerModel = new SpinnerNumberModel( 0.5, 0.0, 1.0, 0.1 );
final JSpinner markerAlignmentYSpinner = new JSpinner( markerAlignmentYSpinnerModel );
markerAlignmentYSpinner.addChangeListener( new ChangeListener() {
public void stateChanged( ChangeEvent e )
{
JSpinner spinner = (JSpinner)e.getSource();
Number value = (Number)spinner.getValue();
float v = value.floatValue();
for( MarkerRenderer markerRenderer : renderers.values() )
{
JComponent component = (JComponent)markerRenderer;
component.setAlignmentY( v );
}
markerBar.revalidate();
markerBar.repaint();
}
} );
JLabel markerAlignmentYLabel = new JLabel( "Alignment Y: " );
markerAlignmentYLabel.setLabelFor( markerAlignmentYSpinner );
final JComboBox markerBorderComboBox = new JComboBox( bordersArray );
markerBorderComboBox.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{
String borderName = (String)markerBorderComboBox.getSelectedItem();
Border border = borders.get( borderName );
for( MarkerRenderer markerRenderer : renderers.values() )
{
JComponent component = (JComponent)markerRenderer;
component.setBorder( border );
}
markerBar.revalidate();
markerBar.repaint();
}
} );
JLabel markerBorderLabel = new JLabel( "Border: " );
markerBorderLabel.setLabelFor( markerBorderComboBox );
final JComboBox markerShapeComboBox = new JComboBox( renderersArray );
markerShapeComboBox.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{
String shape = (String)markerShapeComboBox.getSelectedItem();
MarkerRenderer renderer = renderers.get( shape );
markerBar.setMarkerRenderer( renderer );
}
} );
JLabel markerShapeLabel = new JLabel( "Shape: " );
markerShapeLabel.setLabelFor( markerShapeComboBox );
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.LINE_START;
c.fill = GridBagConstraints.HORIZONTAL;
JPanel markerBarOptionsPanel = new JPanel( new GridBagLayout() );
markerBarOptionsPanel.setBorder( BorderFactory.createTitledBorder( "Marker Bar Options" ));
addOptionComponent( markerBarOptionsPanel, markerBarBorderLabel, c, 0, 0 );
addOptionComponent( markerBarOptionsPanel, markerBarBorderComboBox, c, 1, 0 );
addOptionComponent( markerBarOptionsPanel, markerBarBackgroundLabel, c, 0, 1 );
addOptionComponent( markerBarOptionsPanel, markerBarBackgroundComboBox, c, 1, 1 );
addOptionComponent( markerBarOptionsPanel, markerBarWidthLabel, c, 0, 2 );
addOptionComponent( markerBarOptionsPanel, markerBarWidthSpinner, c, 1, 2 );
JPanel markerOptionsPanel = new JPanel( new GridBagLayout() );
markerOptionsPanel.setBorder( BorderFactory.createTitledBorder( "Marker Options" ));
addOptionComponent( markerOptionsPanel, markerShapeLabel, c, 0, 0 );
addOptionComponent( markerOptionsPanel, markerShapeComboBox, c, 1, 0 );
addOptionComponent( markerOptionsPanel, markerBorderLabel, c, 0, 1 );
addOptionComponent( markerOptionsPanel, markerBorderComboBox, c, 1, 1 );
addOptionComponent( markerOptionsPanel, markerForegroundLabel, c, 0, 2 );
addOptionComponent( markerOptionsPanel, markerForegroundComboBox, c, 1, 2 );
addOptionComponent( markerOptionsPanel, markerWidthLabel, c, 0, 3 );
addOptionComponent( markerOptionsPanel, markerWidthSpinner, c, 1, 3 );
addOptionComponent( markerOptionsPanel, markerHeightLabel, c, 0, 4 );
addOptionComponent( markerOptionsPanel, markerHeightSpinner, c, 1, 4 );
addOptionComponent( markerOptionsPanel, markerAlignmentXLabel, c, 0, 5 );
addOptionComponent( markerOptionsPanel, markerAlignmentXSpinner, c, 1, 5 );
addOptionComponent( markerOptionsPanel, markerAlignmentYLabel, c, 0, 6 );
addOptionComponent( markerOptionsPanel, markerAlignmentYSpinner, c, 1, 6 );
markerBarOptionsPanel.setMaximumSize( markerBarOptionsPanel.getPreferredSize() );
markerOptionsPanel.setMaximumSize( markerOptionsPanel.getPreferredSize() );
optionsPane.add( markerBarOptionsPanel );
optionsPane.add( Box.createVerticalStrut( 20 ));
optionsPane.add( markerOptionsPanel );
BorderLayout borderLayout = new BorderLayout();
panel.setLayout( borderLayout );
panel.add( searchPane, BorderLayout.PAGE_START );
panel.add( optionsPane, BorderLayout.LINE_START );
panel.add( scrollPane, BorderLayout.CENTER );
panel.add( markerBar, BorderLayout.LINE_END );
Container contentPane = getContentPane();
contentPane.add( panel );
final Action searchAction = new AbstractAction( "Search" ) {
public void actionPerformed( ActionEvent e )
{
Highlighter highlighter = textPane.getHighlighter();
highlighter.removeAllHighlights();
model.clear();
Document document = textPane.getDocument();
try
{
String text = document.getText( 0, document.getLength() );
searchInput( text.toLowerCase(), searchField.getText().toLowerCase(), model, textPane, highlightPainter );
}
catch( BadLocationException ble )
{
throw new AssertionError( ble );
}
}
};
DocumentListener documentListener = new DocumentListener() {
public void insertUpdate( DocumentEvent e )
{
searchAction.actionPerformed( null );
}
public void removeUpdate( DocumentEvent e )
{
searchAction.actionPerformed( null );
}
public void changedUpdate( DocumentEvent e )
{
searchAction.actionPerformed( null );
}
};
searchField.getDocument().addDocumentListener( documentListener );
textPane.getDocument().addDocumentListener( documentListener );
scrollPane.addComponentListener( new ComponentAdapter() {
@Override
public void componentResized( ComponentEvent e )
{
searchAction.actionPerformed( null );
}
} );
markerBar.addActionListener( new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
int markerIndex = markerBar.getClickedIndex();
Utility.scrollToMarker( textPane, markerBar, markerIndex );
}
});
setTitle( "Marker Bar Library Demo by Side of Software" );
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setPreferredSize( new Dimension( 600, 400 ));
pack();
searchField.setText( "em" );
}
private static void addOptionComponent( JPanel panel, JComponent component, GridBagConstraints constraints, int x, int y )
{
constraints.gridx = x;
constraints.gridy = y;
panel.add( component, constraints );
}
private static void setSpinnerTextAlignment( JSpinner spinner )
{
JSpinner.DefaultEditor defaultEditor = (JSpinner.DefaultEditor)spinner.getEditor();
JTextField textField = defaultEditor.getTextField();
textField.setHorizontalAlignment( JTextField.RIGHT );
}
private void searchInput( String inputText, String searchText, DefaultListModel model, JTextPane textPane, Highlighter.HighlightPainter painter )
{
if( searchText == null || searchText.length() == 0 )
return;
Highlighter highlighter = textPane.getHighlighter();
String regEx = Pattern.quote( searchText );
Pattern pattern = Pattern.compile( regEx );
Matcher matcher = pattern.matcher( inputText );
int ct = 1;
while( matcher.find() )
{
int start = matcher.start();
int end = matcher.end();
try
{
highlighter.addHighlight( start, end, painter );
double[] markerRange = Utility.textRangeToMarkerRange( textPane, start, end );
Marker marker = new DefaultMarker( markerRange[0], markerRange[1], "Match " + ct );
model.addElement( marker );
ct++;
}
catch( BadLocationException ble )
{
}
}
}
public static void main( String[] args )
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try
{
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
new MarkerBarDemo().setVisible(true);
}
catch( Exception e )
{
throw new AssertionError( e );
}
}
});
}
public static class CircleMarkerRenderer extends JPanel implements MarkerRenderer
{
@Override
public void paint( Graphics g )
{
Dimension size = getSize();
int radius = Math.min( size.width, size.height ) - 1;
Graphics2D g2 = (Graphics2D)g;
g2.addRenderingHints( hints );
g2.setColor( Color.DARK_GRAY );
g2.drawOval( 0, 0, radius, radius );
Color color = getBackground();
g2.setPaint( new GradientPaint( 0.0f, 0.0f, color.brighter(), 0.0f, radius, color.darker() ));
g2.fillOval( 0, 0, radius, radius );
}
public Component getMarkerRendererComponent( JMarkerBar markerBar, int markerIndex, Marker marker )
{
Color color = markerBar.getForeground();
setBackground( color );
return this;
}
}
}