org.apache.batik.apps.svgbrowser.FindDialog Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of batik-svgbrowser Show documentation
Show all versions of batik-svgbrowser Show documentation
Batik SVG Browser Application Library
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.batik.apps.svgbrowser;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import org.apache.batik.bridge.Mark;
import org.apache.batik.bridge.TextNode;
import org.apache.batik.gvt.GVTTreeWalker;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.util.resources.ResourceManager;
import org.apache.batik.util.gui.ExtendedGridBagConstraints;
import org.apache.batik.util.gui.resource.ActionMap;
import org.apache.batik.util.gui.resource.ButtonFactory;
import org.apache.batik.util.gui.resource.MissingListenerException;
/**
* This class represents a Dialog that lets the user searching for text inside
* an SVG document.
*
* @author Thierry Kormann
* @version $Id: FindDialog.java 1733416 2016-03-03 07:07:13Z gadams $
*/
public class FindDialog extends JDialog implements ActionMap {
/**
* The resource file name
*/
protected static final String RESOURCES =
"org.apache.batik.apps.svgbrowser.resources.FindDialog";
// action names
public static final String FIND_ACTION = "FindButtonAction";
public static final String CLEAR_ACTION = "ClearButtonAction";
public static final String CLOSE_ACTION = "CloseButtonAction";
/**
* The resource bundle
*/
protected static ResourceBundle bundle;
/**
* The resource manager
*/
protected static ResourceManager resources;
static {
bundle = ResourceBundle.getBundle(RESOURCES, Locale.getDefault());
resources = new ResourceManager(bundle);
}
/** The button factory */
protected ButtonFactory buttonFactory;
/** The GVT root into which text is searched. */
protected GraphicsNode gvtRoot;
/** The GVTTreeWalker used to scan the GVT Tree. */
protected GVTTreeWalker walker;
/** The current index in the TextNode's string. */
protected int currentIndex;
/** The TextField that owns the text to search. */
protected JTextField search;
/** The next button. */
protected JButton findButton;
/** The next button. */
protected JButton clearButton;
/** The cancel button. */
protected JButton closeButton;
/** The case sensitive button. */
protected JCheckBox caseSensitive;
/** The canvas. */
protected JSVGCanvas svgCanvas;
/** The highlight button. */
protected JRadioButton highlightButton;
/** The highlight and center button. */
protected JRadioButton highlightCenterButton;
/** The highlight center and zoom button. */
protected JRadioButton highlightCenterZoomButton;
/**
* Constructs a new FindDialog
.
*/
public FindDialog(JSVGCanvas svgCanvas) {
this(null, svgCanvas);
}
/**
* Constructs a new FindDialog
.
*/
public FindDialog(Frame owner, JSVGCanvas svgCanvas) {
super(owner, resources.getString("Dialog.title"));
this.svgCanvas = svgCanvas;
buttonFactory = new ButtonFactory(bundle, this);
listeners.put(FIND_ACTION,
new FindButtonAction());
listeners.put(CLEAR_ACTION,
new ClearButtonAction());
listeners.put(CLOSE_ACTION,
new CloseButtonAction());
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
p.add(createFindPanel(), BorderLayout.CENTER);
p.add(createShowResultPanel(), BorderLayout.SOUTH);
getContentPane().add(p, BorderLayout.CENTER);
getContentPane().add(createButtonsPanel(), BorderLayout.SOUTH);
}
/**
* Creates the Find panel.
*/
protected JPanel createFindPanel() {
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(BorderFactory.createTitledBorder
(BorderFactory.createEtchedBorder(),
resources.getString("Panel.title")));
ExtendedGridBagConstraints gbc = new ExtendedGridBagConstraints();
gbc.insets = new Insets(2, 2, 2, 2);
gbc.anchor = ExtendedGridBagConstraints.EAST;
gbc.fill = ExtendedGridBagConstraints.NONE;
gbc.setWeight(0, 0);
gbc.setGridBounds(0, 0, 1, 1);
panel.add(new JLabel(resources.getString("FindLabel.text")), gbc);
gbc.fill = ExtendedGridBagConstraints.HORIZONTAL;
gbc.setWeight(1.0, 0);
gbc.setGridBounds(1, 0, 2, 1);
panel.add(search = new JTextField(20), gbc);
gbc.fill = ExtendedGridBagConstraints.NONE;
gbc.anchor = ExtendedGridBagConstraints.WEST;
gbc.setWeight(0, 0);
gbc.setGridBounds(1, 1, 1, 1);
caseSensitive = buttonFactory.createJCheckBox("CaseSensitiveCheckBox");
panel.add(caseSensitive, gbc);
return panel;
}
protected JPanel createShowResultPanel() {
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(BorderFactory.createTitledBorder
(BorderFactory.createEtchedBorder(),
resources.getString("ShowResultPanel.title")));
ExtendedGridBagConstraints gbc = new ExtendedGridBagConstraints();
gbc.insets = new Insets(2, 2, 2, 2);
gbc.anchor = ExtendedGridBagConstraints.WEST;
gbc.fill = ExtendedGridBagConstraints.NONE;
gbc.setWeight(0, 0);
ButtonGroup grp = new ButtonGroup();
highlightButton = buttonFactory.createJRadioButton("Highlight");
highlightButton.setSelected(true);
grp.add(highlightButton);
gbc.setGridBounds(0, 0, 1, 1);
panel.add(highlightButton, gbc);
highlightCenterButton =
buttonFactory.createJRadioButton("HighlightAndCenter");
grp.add(highlightCenterButton);
gbc.setGridBounds(0, 1, 1, 1);
panel.add(highlightCenterButton, gbc);
highlightCenterZoomButton =
buttonFactory.createJRadioButton("HighlightCenterAndZoom");
grp.add(highlightCenterZoomButton);
gbc.setGridBounds(0, 2, 1, 1);
panel.add(highlightCenterZoomButton, gbc);
return panel;
}
/**
* Creates the buttons panel
*/
protected JPanel createButtonsPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
panel.add(findButton = buttonFactory.createJButton("FindButton"));
panel.add(clearButton = buttonFactory.createJButton("ClearButton"));
panel.add(closeButton = buttonFactory.createJButton("CloseButton"));
return panel;
}
/**
* Sets the graphics node into which text is searched.
* @param gvtRoot the GVT root node
*/
public void setGraphicsNode(GraphicsNode gvtRoot) {
this.gvtRoot = gvtRoot;
if (gvtRoot != null) {
this.walker = new GVTTreeWalker(gvtRoot);
} else {
this.walker = null;
}
}
/**
* Returns the next GraphicsNode that matches the specified string or null
* if any.
*
* @param text the text to match
*/
protected GraphicsNode getNext(String text) {
if (walker == null && gvtRoot != null) {
walker = new GVTTreeWalker(gvtRoot);
}
GraphicsNode gn = walker.getCurrentGraphicsNode();
int index = match(gn, text, currentIndex+text.length());
if (index >= 0) {
currentIndex = index;
} else {
currentIndex = 0;
gn = walker.nextGraphicsNode();
while (gn != null &&
((currentIndex = match(gn, text, currentIndex)) < 0)) {
currentIndex = 0;
gn = walker.nextGraphicsNode();
}
}
return gn;
}
/**
* Returns the index inside the specified TextNode of the
* specified text, or -1 if not found.
*
* @param node the graphics node to check
* @param text the text use to match
* @param index the index from which to start */
protected int match(GraphicsNode node, String text, int index) {
if (!(node instanceof TextNode)
|| !node.isVisible()
|| text == null || text.length() == 0) {
return -1;
}
String s = ((TextNode)node).getText();
if (!caseSensitive.isSelected()) {
s = s.toLowerCase();
text = text.toLowerCase();
}
return s.indexOf(text, index);
}
/**
* Shows the current selected TextNode
.
*/
protected void showSelectedGraphicsNode() {
GraphicsNode gn = walker.getCurrentGraphicsNode();
if (!(gn instanceof TextNode)) {
return;
}
TextNode textNode = (TextNode)gn;
// mark the selection of the substring found
String text = textNode.getText();
String pattern = search.getText();
if (!caseSensitive.isSelected()) {
text = text.toLowerCase();
pattern = pattern.toLowerCase();
}
int end = text.indexOf(pattern, currentIndex);
AttributedCharacterIterator aci =
textNode.getAttributedCharacterIterator();
aci.first();
for (int i=0; i < end; ++i) {
aci.next();
}
Mark startMark = textNode.getMarkerForChar(aci.getIndex(), true);
for (int i = 0; i < pattern.length()-1; ++i) {
aci.next();
}
Mark endMark = textNode.getMarkerForChar(aci.getIndex(), false);
svgCanvas.select(startMark, endMark);
// zoom on the TextNode if needed
if (highlightButton.isSelected()) {
return;
}
// get the highlight shape in GVT root (global) coordinate sytem
Shape s = textNode.getHighlightShape();
AffineTransform at;
if (highlightCenterZoomButton.isSelected()) {
at = svgCanvas.getInitialTransform();
} else {
at = svgCanvas.getRenderingTransform();
}
// get the bounds of the highlight shape in the canvas coordinate system
Rectangle2D gnb = at.createTransformedShape(s).getBounds();
Dimension canvasSize = svgCanvas.getSize();
// translate the highlight region to (0, 0) in the canvas coordinate
// system
AffineTransform Tx = AffineTransform.getTranslateInstance
(-gnb.getX()-gnb.getWidth()/2,
-gnb.getY()-gnb.getHeight()/2);
if (highlightCenterZoomButton.isSelected()) {
// zoom on the highlight shape such as the shape takes x% of the
// canvas size
double sx = canvasSize.width/gnb.getWidth();
double sy = canvasSize.height/gnb.getHeight();
double scale = Math.min(sx, sy) / 8;
if (scale > 1) {
Tx.preConcatenate
(AffineTransform.getScaleInstance(scale, scale));
}
}
Tx.preConcatenate(AffineTransform.getTranslateInstance
(canvasSize.width/2, canvasSize.height/2));
// take into account the initial transform
AffineTransform newRT = new AffineTransform(at);
newRT.preConcatenate(Tx);
// change the rendering transform
svgCanvas.setRenderingTransform(newRT);
}
// ActionMap implementation
/**
* The map that contains the listeners
*/
protected Map listeners = new HashMap(10);
/**
* Returns the action associated with the given string
* or null on error
* @param key the key mapped with the action to get
* @throws MissingListenerException if the action is not found
*/
public Action getAction(String key) throws MissingListenerException {
return (Action)listeners.get(key);
}
//////////////////////////////////////////////////////////////////////////
// Action implementation
//////////////////////////////////////////////////////////////////////////
/**
* The action associated to the 'find' button.
*/
protected class FindButtonAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
String text = search.getText();
if (text == null || text.length() == 0) {
return;
}
GraphicsNode gn = getNext(text);
if (gn != null) {
showSelectedGraphicsNode();
} else {
// end of document reached
walker = null;
JOptionPane.showMessageDialog(FindDialog.this,
resources.getString("End.text"),
resources.getString("End.title"),
JOptionPane.INFORMATION_MESSAGE);
}
}
}
/**
* The action associated to the 'clear' button.
*/
protected class ClearButtonAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
search.setText(null);
walker = null;
}
}
/**
* The action associated to the 'close' button.
*/
protected class CloseButtonAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
dispose();
}
}
}