📄 helpdialog.java
字号:
/*
* 11/14/2003
*
* HelpDialog.java - A "Help" dialog box for use in Java applications.
* Copyright (C) 2003 Robert Futrell
* email@address.com
* www.website.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.fife.help;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.text.Document;
import javax.swing.text.Position;
import javax.swing.text.html.HTMLDocument;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.fife.RListSelectionModel;
import org.fife.RTreeSelectionModel;
import org.fife.RUtilities;
import org.fife.ui.RButton;
import org.fife.ui.RScrollPane;
import org.fife.ui.StatusBar;
import org.fife.ui.UIUtilities;
import org.fife.ui.app.GUIApplication;
/**
* A help dialog similar to those found in most Microsoft Windows programs.
* The dialog contains a pane with Contents, Index, and Search tabs, each
* of which yields the expected way to find help. Help pages should be
* HTML.<p>
*
* Features of <code>HelpDialog</code> include:
* <ul>
* <li>Contents/Index/Search pane - Works just like most Windows programs'
* Contents/Index/Search. Pick your favorite way to find help.</li>
* <li>Search/Index options automatically find all help documents (which you
* pass to HelpDialog) containing the word/phrase for which the user
* wants to find help.</li>
* <li>History - The HelpDialog class will remember the help documents
* the user has previously visited, and the user can use the Back and
* Forward buttons to easily traverse previous help.
* <li>HTML Help - Help documents are HTML, to allow for easy formatting.
* </li>
* </ul>
*
* Notes:<br>
* In your HTML documentation, anchors can be used as hyperlinks; however,
* if you have a hyperlink that is simply an anchor to another place in the
* current HTML file (<code>a href="#anchor"</code> as opposed to
* <code>a href="file2.html#anchor"</code>), you need to explicitly state
* that the anchor is in the current file (i.e.,
* <code>a href="file2.html#anchor"</code>) for this class to parse it
* correctly.
*
* @author Robert Futrell
* @version 1.0
*/
public class HelpDialog extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = -3120550294746862067L;
private GUIApplication guiApp;
private JTabbedPane tabbedPane;
private DefaultMutableTreeNode root;
private JTree tocTree; // Tree laying out the table of contents.
private boolean rootVisible;
private JEditorPane editorPane; // Right-hand pane; displays actual help.
private JList indexList; // List of all elements in our index.
private JButton indexDisplayButton; // Display button on the "Index" tab.
private JTextField indexField; // Text field in which they can type in an index value.
private JList searchList; // List of all found documents on Search panel.
private JButton searchDisplayButton; // Display button on the "Search" tab.
private JButton listTopicsButton; // Button to display help topics matching searchString.
private JTextField searchField; // Text field tp type a string to search for.
private JButton backButton; // "Back" button on toolbar.
private JButton forwardButton; // "Forward" button on toolbar.
private String searchString; // What to look for in the search tab.
private boolean highlightSearchString;
private ArrayList history; // All HelpTreeNodes the user has previoiusly viewed.
private int historyPos; // Position in history array we're at.
private boolean updateHistory; // If true, selecting a tocTree node updates history.
private boolean clickedOnTOCTree; // Whether they clicked on tocTree to go to a help page.
private JLabel keywordToFindLabel;
private JLabel keywordToFindLabel2;
private JLabel topicToDisplayLabel;
private String baseDir;
private URL baseURL;
private ResourceBundle treeBundle;
private String[] indexElements;
private static final String HTML_TYPE = "text/html";
private static final String TEXT_TYPE = "text/plain";
private static final String NO_MATCH_HTML = "<html><body>" +
"<h2>No matches found in Help.</h2>" +
"<HR ALIGN=\"center\" WIDTH=\"100%\">" +
"</body></html>";
// tags used in nodes.
private static final String INDEXITEMS = "IndexItems";
private static final String NAME = "name";
private static final String PAGE = "Page";
private static final String PAGE_VALUE = "page";
private static final String PROPERTIES_FILE = "PropertiesFile";
private static final String ROOT_ELEMENT = "HelpDialogContents";
private static final String TREE_NODE = "Node";
private static final String TREE_ROOT_NODE = "RootNode";
private static final String VISIBLE = "visible";
/*****************************************************************************/
/**
* Creates a new help dialog.
*
* @param owner The frame that owns this dialog.
* @param contentsFile The XML file specifying the structure of this
* online help.
* @param baseDir The directory in which all help stuff is stored. This
* will also be used as the base directory for relative
* links in the HTML of the online help.
*/
public HelpDialog(GUIApplication owner, String contentsFile,
String baseDir) {
// Make the title of the dialog appropriate.
super();
this.guiApp = owner;
// Create some stuff we'll need below.
ResourceBundle msg = getHelpBundle();
HelpListener listener = new HelpListener();
// Set the base URL.
this.baseDir = baseDir;
try {
setBaseURL(new URL("file:///" + baseDir));
} catch (Exception e) {
guiApp.displayException(this, e);
}
// Create the DefaultMutableTreeNode tree that will be our "tree" of
// help pages.
createRoot(contentsFile);
// Make a text area for the right-component of the split pane (the HTML help).
editorPane = new JEditorPane();
editorPane.setFont(new Font("monospaced", Font.PLAIN, 12));
editorPane.setPreferredSize(new Dimension(81*8,300));
editorPane.setEditable(false);
editorPane.addHyperlinkListener(listener);
editorPane.setContentType("text/html");
editorPane.getDocument().putProperty("IgnoreCharsetDirective", Boolean.TRUE);
// Create contents subpanel for the left-component of the split pane.
JPanel tocPanel = UIUtilities.createTabbedPanePanel();
tocPanel.setLayout(new BoxLayout(tocPanel, BoxLayout.Y_AXIS));
tocPanel.setBorder(UIUtilities.getEmpty5Border());
tocTree = new JTree(root);
tocTree.setRootVisible(rootVisible); // Set in initializeFromXMLFile().
tocTree.setSelectionModel(new RTreeSelectionModel());
tocTree.setToggleClickCount(1);
tocTree.addTreeSelectionListener(listener);
tocTree.addKeyListener(listener);
JScrollPane scrollPane = new RScrollPane(1,1, tocTree);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
tocPanel.add(scrollPane);
// Create the index subpanel for the left-component of the split pane.
JPanel indexPanel = UIUtilities.createTabbedPanePanel();
indexPanel.setLayout(new BoxLayout(indexPanel, BoxLayout.Y_AXIS));
indexPanel.setBorder(UIUtilities.getEmpty5Border());
JPanel indexInputPanel = UIUtilities.createTabbedPanePanel();
indexInputPanel.setLayout(new BoxLayout(indexInputPanel, BoxLayout.Y_AXIS));
keywordToFindLabel = new JLabel(msg.getString("KeywordLabel"));
JPanel ilPanel = UIUtilities.createTabbedPanePanel();
ilPanel.setLayout(new BoxLayout(ilPanel, BoxLayout.X_AXIS));
ilPanel.add(keywordToFindLabel);
ilPanel.add(Box.createHorizontalGlue());
indexField = new JTextField();
indexField.setMaximumSize(new Dimension(3000,20));
indexField.getDocument().addDocumentListener(listener);
indexField.addKeyListener(listener);
indexInputPanel.add(ilPanel);
indexInputPanel.add(indexField);
indexList = new JList(indexElements);
indexElements = null;
indexList.addMouseListener(listener);
indexList.addKeyListener(listener);
indexList.setSelectionModel(new RListSelectionModel());
JScrollPane indexScrollPane = new RScrollPane(1,1, indexList);
indexScrollPane.setPreferredSize(new Dimension(100,200));
indexScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel indexButtonPanel = UIUtilities.createTabbedPanePanel();
indexButtonPanel.setLayout(new BoxLayout(indexButtonPanel, BoxLayout.X_AXIS));
indexDisplayButton = UIUtilities.createTabbedPaneButton(msg.getString("Display"));
indexDisplayButton.setActionCommand("Display");
indexDisplayButton.addActionListener(this);
indexButtonPanel.add(Box.createHorizontalGlue());
indexButtonPanel.add(indexDisplayButton);
indexPanel.add(indexInputPanel);
indexPanel.add(Box.createVerticalStrut(8));
indexPanel.add(indexScrollPane);
indexPanel.add(Box.createVerticalStrut(8));
indexPanel.add(indexButtonPanel);
indexList.setSelectedIndex(0);
// Create the search subpanel for the left-component of the split pane.
JPanel searchPanel = UIUtilities.createTabbedPanePanel();
searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.Y_AXIS));
searchPanel.setBorder(UIUtilities.getEmpty5Border());
JPanel searchInputPanel = UIUtilities.createTabbedPanePanel();
searchInputPanel.setLayout(new BoxLayout(searchInputPanel, BoxLayout.Y_AXIS));
keywordToFindLabel2 = new JLabel(msg.getString("KeywordLabel"));
JPanel slPanel = UIUtilities.createTabbedPanePanel();
slPanel.setLayout(new BoxLayout(slPanel, BoxLayout.X_AXIS));
slPanel.add(keywordToFindLabel2);
slPanel.add(Box.createHorizontalGlue());
searchField = new JTextField();
searchField.setMaximumSize(new Dimension(3000,20));
searchField.getDocument().addDocumentListener(listener);
searchField.addKeyListener(listener);
listTopicsButton = UIUtilities.createTabbedPaneButton(msg.getString("ListTopics"));
listTopicsButton.setActionCommand("ListTopics");
listTopicsButton.addActionListener(this);
listTopicsButton.setEnabled(false);
JPanel ltbPanel = UIUtilities.createTabbedPanePanel();
ltbPanel.setLayout(new BoxLayout(ltbPanel, BoxLayout.X_AXIS));
ltbPanel.add(Box.createHorizontalGlue());
ltbPanel.add(listTopicsButton);
topicToDisplayLabel = new JLabel(msg.getString("TopicToDisplay"));
JPanel stlPanel = UIUtilities.createTabbedPanePanel();
stlPanel.setLayout(new BoxLayout(stlPanel, BoxLayout.X_AXIS));
stlPanel.add(topicToDisplayLabel);
stlPanel.add(Box.createHorizontalGlue());
searchInputPanel.add(slPanel);
searchInputPanel.add(Box.createHorizontalGlue());
searchInputPanel.add(searchField);
searchInputPanel.add(Box.createVerticalStrut(5));
searchInputPanel.add(ltbPanel);
searchList = new JList();
searchList.addMouseListener(listener);
searchList.addKeyListener(listener);
searchList.setSelectionModel(new RListSelectionModel());
JScrollPane searchScrollPane = new RScrollPane(1,1, searchList);
searchScrollPane.setPreferredSize(new Dimension(100,200));
searchScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel searchButtonPanel = UIUtilities.createTabbedPanePanel();
searchButtonPanel.setLayout(new BoxLayout(searchButtonPanel, BoxLayout.X_AXIS));
searchDisplayButton = UIUtilities.createTabbedPaneButton(msg.getString("Display"));
searchDisplayButton.setActionCommand("Display");
searchDisplayButton.addActionListener(this);
searchDisplayButton.setEnabled(false);
searchButtonPanel.add(Box.createHorizontalGlue());
searchButtonPanel.add(searchDisplayButton);
searchPanel.add(searchInputPanel);
searchPanel.add(Box.createVerticalStrut(5));
searchPanel.add(stlPanel);
searchPanel.add(searchScrollPane);
searchPanel.add(Box.createVerticalStrut(8));
searchPanel.add(searchButtonPanel);
// Put the subpanels for the left frame into a tabbed pane.
tabbedPane = new JTabbedPane();
tabbedPane.addChangeListener(listener);
tabbedPane.addTab(msg.getString("Contents"), tocPanel);
tabbedPane.addTab(msg.getString("Index"), indexPanel);
tabbedPane.addTab(msg.getString("Search"), searchPanel);
// Create the Help dialog's left panel.
JPanel leftPanel = new JPanel(new GridLayout(1,1));
leftPanel.setPreferredSize(new Dimension(300,400));
leftPanel.add(tabbedPane);
// Create the split pane.
JSplitPane splitPane = new JSplitPane();
splitPane.setLeftComponent(leftPanel);
JScrollPane rightScrollPane = new RScrollPane(400,200, editorPane);
rightScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
splitPane.setRightComponent(rightScrollPane);
// Make the toolbar.
JToolBar toolBar = new JToolBar();
backButton = new RButton(msg.getString("Back"));
backButton.setActionCommand("Back");
backButton.addActionListener(this);
backButton.setEnabled(false);
toolBar.add(backButton);
forwardButton = new RButton(msg.getString("Forward"));
forwardButton.setActionCommand("Forward");
forwardButton.addActionListener(this);
forwardButton.setEnabled(false);
toolBar.add(forwardButton);
toolBar.setFloatable(false);
// Make our help dialog!
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(toolBar, BorderLayout.NORTH);
contentPane.add(splitPane, BorderLayout.CENTER);
contentPane.add(new StatusBar(""), BorderLayout.SOUTH);
pack();
// Initially, the use hasn't viewed any nodes.
history = new ArrayList();
updateHistory = true;
clickedOnTOCTree = true; // Initially, we assume they click on tocTree first.
historyPos = -1; // Initially, there is no history to point to.
// By default, our tree is open to the first node.
// This will select it in the tocTree and bring up first help page.
tocTree.setSelectionRow(0);
// We must reset backButton to be disabled, as setSelectionRow enables it.
backButton.setEnabled(false);
// Get ready to go!
setTitle(msg.getString("Title"));
msg = null; // May help in GC.
setIconImage(owner.getIconImage());
pack();
}
/*****************************************************************************/
// Callback for when an action occurs.
public void actionPerformed(ActionEvent e) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -