/*
* HelpTOCPanel.java - Help table of contents
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 1999, 2004 Slava Pestov
*
* 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.gjt.sp.jedit.help;
//{{{ Imports
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import org.gjt.sp.util.ThreadUtilities;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.gjt.sp.jedit.browser.FileCellRenderer; // for icons
import org.gjt.sp.jedit.*;
import org.gjt.sp.util.EnhancedTreeCellRenderer;
import org.gjt.sp.util.Log;
import org.gjt.sp.util.StandardUtilities;
import org.gjt.sp.util.XMLUtilities;
import static javax.swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION;
//}}}
public class HelpTOCPanel extends JPanel
{
//{{{ HelpTOCPanel constructor
public HelpTOCPanel(HelpViewerInterface helpViewer)
{
super(new BorderLayout());
this.helpViewer = helpViewer;
nodes = new HashMap<String,DefaultMutableTreeNode>();
toc = new TOCTree();
// looks bad with the OS X L&F, apparently...
if(!OperatingSystem.isMacOSLF())
toc.putClientProperty("JTree.lineStyle", "Angled");
toc.setCellRenderer(new TOCCellRenderer());
toc.setEditable(false);
toc.setShowsRootHandles(true);
add(BorderLayout.CENTER,new JScrollPane(toc));
load();
} //}}}
//{{{ selectNode() method
public void selectNode(String shortURL)
{
if(tocModel == null)
return;
final DefaultMutableTreeNode node =
nodes.get(shortURL);
if(node == null)
return;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
TreePath path = new TreePath(tocModel.getPathToRoot(node));
toc.expandPath(path);
toc.setSelectionPath(path);
toc.scrollPathToVisible(path);
}
});
} //}}}
//{{{ load() method
public void load()
{
DefaultTreeModel empty = new DefaultTreeModel(
new DefaultMutableTreeNode(
jEdit.getProperty("helpviewer.toc.loading")));
toc.setModel(empty);
toc.setRootVisible(true);
ThreadUtilities.runInBackground(new Runnable()
{
public void run()
{
createTOC();
tocModel.reload(tocRoot);
toc.setModel(tocModel);
toc.setRootVisible(false);
for(int i = 0; i <tocRoot.getChildCount(); i++)
{
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)
tocRoot.getChildAt(i);
toc.expandPath(new TreePath(
node.getPath()));
}
if(helpViewer.getShortURL() != null)
selectNode(helpViewer.getShortURL());
}
});
} //}}}
//{{{ Private members
private HelpViewerInterface helpViewer;
private DefaultTreeModel tocModel;
private DefaultMutableTreeNode tocRoot;
private JTree toc;
private Map<String, DefaultMutableTreeNode> nodes;
//{{{ createNode() method
private DefaultMutableTreeNode createNode(String href, String title)
{
DefaultMutableTreeNode node = new DefaultMutableTreeNode(
new HelpNode(href,title),true);
nodes.put(href,node);
return node;
} //}}}
//{{{ createTOC() method
private void createTOC()
{
EditPlugin[] plugins = jEdit.getPlugins();
Arrays.sort(plugins,new PluginCompare());
tocRoot = new DefaultMutableTreeNode();
tocRoot.add(createNode("welcome.html",
jEdit.getProperty("helpviewer.toc.welcome")));
tocRoot.add(createNode("README.txt",
jEdit.getProperty("helpviewer.toc.readme")));
tocRoot.add(createNode("CHANGES.txt",
jEdit.getProperty("helpviewer.toc.changes")));
tocRoot.add(createNode("TODO.txt",
jEdit.getProperty("helpviewer.toc.todo")));
tocRoot.add(createNode("COPYING.txt",
jEdit.getProperty("helpviewer.toc.copying")));
tocRoot.add(createNode("COPYING.DOC.txt",
jEdit.getProperty("helpviewer.toc.copying-doc")));
tocRoot.add(createNode("Apache.LICENSE.txt",
jEdit.getProperty("helpviewer.toc.copying-apache")));
tocRoot.add(createNode("COPYING.PLUGINS.txt",
jEdit.getProperty("helpviewer.toc.copying-plugins")));
loadTOC(tocRoot,"whatsnew/toc.xml");
loadTOC(tocRoot,"users-guide/toc.xml");
loadTOC(tocRoot,"FAQ/toc.xml");
DefaultMutableTreeNode pluginTree = new DefaultMutableTreeNode(
jEdit.getProperty("helpviewer.toc.plugins"),true);
for (EditPlugin plugin : plugins)
{
String name = plugin.getClassName();
String docs = jEdit.getProperty("plugin." + name + ".docs");
String label = jEdit.getProperty("plugin." + name + ".name");
if (label != null && docs != null)
{
String path = plugin.getPluginJAR().getClassLoader().getResourceAsPath(docs);
pluginTree.add(createNode(path, label));
}
}
if(pluginTree.getChildCount() != 0)
tocRoot.add(pluginTree);
else
{
// so that HelpViewer constructor doesn't try to expand
pluginTree = null;
}
loadTOC(tocRoot,"api/toc.xml");
tocModel = new DefaultTreeModel(tocRoot);
} //}}}
//{{{ loadTOC() method
private void loadTOC(DefaultMutableTreeNode root, String path)
{
TOCHandler h = new TOCHandler(root,MiscUtilities.getParentOfPath(path));
try
{
XMLUtilities.parseXML(
new URL(helpViewer.getBaseURL()
+ '/' + path).openStream(), h);
}
catch(FileNotFoundException e)
{
/* it is acceptable only for the API TOC :
the user can choose not to install them
*/
if("api/toc.xml".equals(path))
{
Log.log(Log.NOTICE,this,
"The API docs for jEdit will not be available (reinstall jEdit if you want them)");
root.add(
createNode("http://www.jedit.org/api/overview-summary.html",
jEdit.getProperty("helpviewer.toc.online-apidocs")));
}
else
{
Log.log(Log.ERROR,this,e);
}
}
catch(IOException e)
{
Log.log(Log.ERROR,this,e);
}
} //}}}
//}}}
//{{{ HelpNode class
static class HelpNode
{
String href, title;
//{{{ HelpNode constructor
HelpNode(String href, String title)
{
this.href = href;
this.title = title;
} //}}}
//{{{ toString() method
public String toString()
{
return title;
} //}}}
} //}}}
//{{{ TOCHandler class
class TOCHandler extends DefaultHandler
{
String dir;
//{{{ TOCHandler constructor
TOCHandler(DefaultMutableTreeNode root, String dir)
{
nodes = new Stack<DefaultMutableTreeNode>();
node = root;
this.dir = dir;
} //}}}
//{{{ characters() method
public void characters(char[] c, int off, int len)
{
if(tag.equals("TITLE"))
{
boolean firstNonWhitespace = false;
for(int i = 0; i < len; i++)
{
char ch = c[off + i];
if (!firstNonWhitespace && Character.isWhitespace(ch)) continue;
firstNonWhitespace = true;
title.append(ch);
}
}
} //}}}
//{{{ startElement() method
public void startElement(String uri, String localName,
String name, Attributes attrs)
{
tag = name;
if (name.equals("ENTRY"))
href = attrs.getValue("HREF");
} //}}}
//{{{ endElement() method
public void endElement(String uri, String localName, String name)
{
if(name == null)
return;
if(name.equals("TITLE"))
{
DefaultMutableTreeNode newNode = createNode(
dir + href,title.toString());
node.add(newNode);
nodes.push(node);
node = newNode;
title.setLength(0);
}
else if(name.equals("ENTRY"))
{
node = nodes.pop();
href = null;
}
} //}}}
//{{{ Private members
private String tag;
private StringBuilder title = new StringBuilder();
private String href;
private DefaultMutableTreeNode node;
private Stack<DefaultMutableTreeNode> nodes;
//}}}
} //}}}
//{{{ TOCTree class
class TOCTree extends JTree
{
//{{{ TOCTree constructor
TOCTree()
{
ToolTipManager.sharedInstance().registerComponent(this);
selectionModel.setSelectionMode(SINGLE_TREE_SELECTION);
} //}}}
//{{{ getToolTipText() method
public final String getToolTipText(MouseEvent evt)
{
TreePath path = getPathForLocation(evt.getX(), evt.getY());
if(path != null)
{
Rectangle cellRect = getPathBounds(path);
if(cellRect != null && !cellRectIsVisible(cellRect))
return path.getLastPathComponent().toString();
}
return null;
} //}}}
//{{{ getToolTipLocation() method
/* public final Point getToolTipLocation(MouseEvent evt)
{
TreePath path = getPathForLocation(evt.getX(), evt.getY());
if(path != null)
{
Rectangle cellRect = getPathBounds(path);
if(cellRect != null && !cellRectIsVisible(cellRect))
{
return new Point(cellRect.x + 14, cellRect.y);
}
}
return null;
} */ //}}}
//{{{ processKeyEvent() method
public void processKeyEvent(KeyEvent evt)
{
if ((KeyEvent.KEY_PRESSED == evt.getID()) &&
(KeyEvent.VK_ENTER == evt.getKeyCode()))
{
TreePath path = getSelectionPath();
if(path != null)
{
Object obj = ((DefaultMutableTreeNode)
path.getLastPathComponent())
.getUserObject();
if(!(obj instanceof HelpNode))
{
this.expandPath(path);
return;
}
HelpNode node = (HelpNode)obj;
helpViewer.gotoURL(node.href,true,0);
}
evt.consume();
}
else
{
super.processKeyEvent(evt);
}
} //}}}
//{{{ processMouseEvent() method
protected void processMouseEvent(MouseEvent evt)
{
//ToolTipManager ttm = ToolTipManager.sharedInstance();
switch(evt.getID())
{
/* case MouseEvent.MOUSE_ENTERED:
toolTipInitialDelay = ttm.getInitialDelay();
toolTipReshowDelay = ttm.getReshowDelay();
ttm.setInitialDelay(200);
ttm.setReshowDelay(0);
super.processMouseEvent(evt);
break;
case MouseEvent.MOUSE_EXITED:
ttm.setInitialDelay(toolTipInitialDelay);
ttm.setReshowDelay(toolTipReshowDelay);
super.processMouseEvent(evt);
break; */
case MouseEvent.MOUSE_CLICKED:
TreePath path = getPathForLocation(evt.getX(),evt.getY());
if(path != null)
{
if(!isPathSelected(path))
setSelectionPath(path);
Object obj = ((DefaultMutableTreeNode)
path.getLastPathComponent())
.getUserObject();
if(!(obj instanceof HelpNode))
{
this.expandPath(path);
return;
}
HelpNode node = (HelpNode)obj;
helpViewer.gotoURL(node.href,true,0);
}
super.processMouseEvent(evt);
break;
default:
super.processMouseEvent(evt);
break;
}
} //}}}
//{{{ cellRectIsVisible() method
private boolean cellRectIsVisible(Rectangle cellRect)
{
Rectangle vr = TOCTree.this.getVisibleRect();
return vr.contains(cellRect.x,cellRect.y) &&
vr.contains(cellRect.x + cellRect.width,
cellRect.y + cellRect.height);
} //}}}
} //}}}
//{{{ TOCCellRenderer class
static class TOCCellRenderer extends EnhancedTreeCellRenderer
{
@Override
protected TreeCellRenderer newInstance()
{
return new TOCCellRenderer();
}
@Override
protected void configureTreeCellRendererComponent(JTree tree,
Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean focus)
{
setIcon(leaf ? FileCellRenderer.fileIcon
: (expanded ? FileCellRenderer.openDirIcon
: FileCellRenderer.dirIcon));
setBorder(border);
}
EmptyBorder border = new EmptyBorder(1,0,1,1);
} //}}}
//{{{ PluginCompare class
static class PluginCompare implements Comparator<EditPlugin>
{
public int compare(EditPlugin p1, EditPlugin p2)
{
return StandardUtilities.compareStrings(
jEdit.getProperty("plugin." + p1.getClassName() + ".name"),
jEdit.getProperty("plugin." + p2.getClassName() + ".name"),
true);
}
} //}}}
}