001: /*
002: * HelpTOCPanel.java - Help table of contents
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 1999, 2004 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.help;
024:
025: //{{{ Imports
026: import javax.swing.*;
027: import javax.swing.border.*;
028: import javax.swing.tree.*;
029: import java.awt.*;
030: import java.awt.event.*;
031: import java.io.*;
032: import java.net.*;
033: import java.util.*;
034:
035: import org.xml.sax.Attributes;
036: import org.xml.sax.helpers.DefaultHandler;
037:
038: import org.gjt.sp.jedit.browser.FileCellRenderer; // for icons
039: import org.gjt.sp.jedit.io.VFSManager;
040: import org.gjt.sp.jedit.*;
041: import org.gjt.sp.util.Log;
042: import org.gjt.sp.util.StandardUtilities;
043: import org.gjt.sp.util.XMLUtilities;
044:
045: import static javax.swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION;
046:
047: //}}}
048:
049: public class HelpTOCPanel extends JPanel {
050: //{{{ HelpTOCPanel constructor
051: public HelpTOCPanel(HelpViewerInterface helpViewer) {
052: super (new BorderLayout());
053:
054: this .helpViewer = helpViewer;
055: nodes = new Hashtable();
056:
057: toc = new TOCTree();
058:
059: // looks bad with the OS X L&F, apparently...
060: if (!OperatingSystem.isMacOSLF())
061: toc.putClientProperty("JTree.lineStyle", "Angled");
062:
063: toc.setCellRenderer(new TOCCellRenderer());
064: toc.setEditable(false);
065: toc.setShowsRootHandles(true);
066:
067: add(BorderLayout.CENTER, new JScrollPane(toc));
068:
069: load();
070: } //}}}
071:
072: //{{{ selectNode() method
073: public void selectNode(String shortURL) {
074: if (tocModel == null)
075: return;
076:
077: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
078: .get(shortURL);
079:
080: if (node == null)
081: return;
082:
083: TreePath path = new TreePath(tocModel.getPathToRoot(node));
084: toc.expandPath(path);
085: toc.setSelectionPath(path);
086: toc.scrollPathToVisible(path);
087: } //}}}
088:
089: //{{{ load() method
090: public void load() {
091: DefaultTreeModel empty = new DefaultTreeModel(
092: new DefaultMutableTreeNode(jEdit
093: .getProperty("helpviewer.toc.loading")));
094: toc.setModel(empty);
095: toc.setRootVisible(true);
096:
097: VFSManager.runInWorkThread(new Runnable() {
098: public void run() {
099: createTOC();
100: tocModel.reload(tocRoot);
101: toc.setModel(tocModel);
102: toc.setRootVisible(false);
103: for (int i = 0; i < tocRoot.getChildCount(); i++) {
104: DefaultMutableTreeNode node = (DefaultMutableTreeNode) tocRoot
105: .getChildAt(i);
106: toc.expandPath(new TreePath(node.getPath()));
107: }
108: if (helpViewer.getShortURL() != null)
109: selectNode(helpViewer.getShortURL());
110: }
111: });
112: } //}}}
113:
114: //{{{ Private members
115: private HelpViewerInterface helpViewer;
116: private DefaultTreeModel tocModel;
117: private DefaultMutableTreeNode tocRoot;
118: private JTree toc;
119: private Hashtable nodes;
120:
121: //{{{ createNode() method
122: private DefaultMutableTreeNode createNode(String href, String title) {
123: DefaultMutableTreeNode node = new DefaultMutableTreeNode(
124: new HelpNode(href, title), true);
125: nodes.put(href, node);
126: return node;
127: } //}}}
128:
129: //{{{ createTOC() method
130: private void createTOC() {
131: EditPlugin[] plugins = jEdit.getPlugins();
132: Arrays.sort(plugins, new PluginCompare());
133: tocRoot = new DefaultMutableTreeNode();
134:
135: tocRoot.add(createNode("welcome.html", jEdit
136: .getProperty("helpviewer.toc.welcome")));
137:
138: tocRoot.add(createNode("README.txt", jEdit
139: .getProperty("helpviewer.toc.readme")));
140: tocRoot.add(createNode("CHANGES.txt", jEdit
141: .getProperty("helpviewer.toc.changes")));
142: tocRoot.add(createNode("TODO.txt", jEdit
143: .getProperty("helpviewer.toc.todo")));
144: tocRoot.add(createNode("COPYING.txt", jEdit
145: .getProperty("helpviewer.toc.copying")));
146: tocRoot.add(createNode("COPYING.DOC.txt", jEdit
147: .getProperty("helpviewer.toc.copying-doc")));
148: tocRoot.add(createNode("Apache.LICENSE.txt", jEdit
149: .getProperty("helpviewer.toc.copying-apache")));
150: tocRoot.add(createNode("COPYING.PLUGINS.txt", jEdit
151: .getProperty("helpviewer.toc.copying-plugins")));
152:
153: loadTOC(tocRoot, "news43/toc.xml");
154: loadTOC(tocRoot, "users-guide/toc.xml");
155: loadTOC(tocRoot, "FAQ/toc.xml");
156:
157: DefaultMutableTreeNode pluginTree = new DefaultMutableTreeNode(
158: jEdit.getProperty("helpviewer.toc.plugins"), true);
159:
160: for (int i = 0; i < plugins.length; i++) {
161: EditPlugin plugin = plugins[i];
162:
163: String name = plugin.getClassName();
164:
165: String docs = jEdit.getProperty("plugin." + name + ".docs");
166: String label = jEdit
167: .getProperty("plugin." + name + ".name");
168: if (docs != null) {
169: if (label != null && docs != null) {
170: String path = plugin.getPluginJAR()
171: .getClassLoader().getResourceAsPath(docs);
172: pluginTree.add(createNode(path, label));
173: }
174: }
175: }
176:
177: if (pluginTree.getChildCount() != 0)
178: tocRoot.add(pluginTree);
179: else {
180: // so that HelpViewer constructor doesn't try to expand
181: pluginTree = null;
182: }
183: loadTOC(tocRoot, "api/toc.xml");
184: tocModel = new DefaultTreeModel(tocRoot);
185: } //}}}
186:
187: //{{{ loadTOC() method
188: private void loadTOC(DefaultMutableTreeNode root, String path) {
189: TOCHandler h = new TOCHandler(root, MiscUtilities
190: .getParentOfPath(path));
191: try {
192: XMLUtilities.parseXML(new URL(helpViewer.getBaseURL() + '/'
193: + path).openStream(), h);
194: } catch (IOException e) {
195: Log.log(Log.ERROR, this , e);
196: }
197: } //}}}
198:
199: //}}}
200:
201: //{{{ HelpNode class
202: static class HelpNode {
203: String href, title;
204:
205: //{{{ HelpNode constructor
206: HelpNode(String href, String title) {
207: this .href = href;
208: this .title = title;
209: } //}}}
210:
211: //{{{ toString() method
212: public String toString() {
213: return title;
214: } //}}}
215: } //}}}
216:
217: //{{{ TOCHandler class
218: class TOCHandler extends DefaultHandler {
219: String dir;
220:
221: //{{{ TOCHandler constructor
222: TOCHandler(DefaultMutableTreeNode root, String dir) {
223: nodes = new Stack();
224: node = root;
225: this .dir = dir;
226: } //}}}
227:
228: //{{{ characters() method
229: public void characters(char[] c, int off, int len) {
230: if (tag.equals("TITLE")) {
231: boolean firstNonWhitespace = false;
232: for (int i = 0; i < len; i++) {
233: char ch = c[off + i];
234: if (!firstNonWhitespace
235: && Character.isWhitespace(ch))
236: continue;
237: firstNonWhitespace = true;
238: title.append(ch);
239: }
240: }
241:
242: } //}}}
243:
244: //{{{ startElement() method
245: public void startElement(String uri, String localName,
246: String name, Attributes attrs) {
247: tag = name;
248: if (name.equals("ENTRY"))
249: href = attrs.getValue("HREF");
250: } //}}}
251:
252: //{{{ endElement() method
253: public void endElement(String uri, String localName, String name) {
254: if (name == null)
255: return;
256:
257: if (name.equals("TITLE")) {
258: DefaultMutableTreeNode newNode = createNode(dir + href,
259: title.toString());
260: node.add(newNode);
261: nodes.push(node);
262: node = newNode;
263: title.setLength(0);
264: } else if (name.equals("ENTRY")) {
265: node = (DefaultMutableTreeNode) nodes.pop();
266: href = null;
267: }
268: } //}}}
269:
270: //{{{ Private members
271: private String tag;
272: private StringBuffer title = new StringBuffer();
273: private String href;
274: private DefaultMutableTreeNode node;
275: private Stack nodes;
276: //}}}
277: } //}}}
278:
279: //{{{ TOCTree class
280: class TOCTree extends JTree {
281: //{{{ TOCTree constructor
282: TOCTree() {
283: ToolTipManager.sharedInstance().registerComponent(this );
284: selectionModel.setSelectionMode(SINGLE_TREE_SELECTION);
285: } //}}}
286:
287: //{{{ getToolTipText() method
288: public final String getToolTipText(MouseEvent evt) {
289: TreePath path = getPathForLocation(evt.getX(), evt.getY());
290: if (path != null) {
291: Rectangle cellRect = getPathBounds(path);
292: if (cellRect != null && !cellRectIsVisible(cellRect))
293: return path.getLastPathComponent().toString();
294: }
295: return null;
296: } //}}}
297:
298: //{{{ getToolTipLocation() method
299: /* public final Point getToolTipLocation(MouseEvent evt)
300: {
301: TreePath path = getPathForLocation(evt.getX(), evt.getY());
302: if(path != null)
303: {
304: Rectangle cellRect = getPathBounds(path);
305: if(cellRect != null && !cellRectIsVisible(cellRect))
306: {
307: return new Point(cellRect.x + 14, cellRect.y);
308: }
309: }
310: return null;
311: } *///}}}
312: //{{{ processKeyEvent() method
313: public void processKeyEvent(KeyEvent evt) {
314: if ((KeyEvent.KEY_PRESSED == evt.getID())
315: && (KeyEvent.VK_ENTER == evt.getKeyCode())) {
316: TreePath path = getSelectionPath();
317: if (path != null) {
318: Object obj = ((DefaultMutableTreeNode) path
319: .getLastPathComponent()).getUserObject();
320: if (!(obj instanceof HelpNode)) {
321: this .expandPath(path);
322: return;
323: }
324:
325: HelpNode node = (HelpNode) obj;
326: helpViewer.gotoURL(node.href, true, 0);
327: }
328: evt.consume();
329: } else {
330: super .processKeyEvent(evt);
331: }
332: } //}}}
333:
334: //{{{ processMouseEvent() method
335: protected void processMouseEvent(MouseEvent evt) {
336: //ToolTipManager ttm = ToolTipManager.sharedInstance();
337:
338: switch (evt.getID()) {
339: /* case MouseEvent.MOUSE_ENTERED:
340: toolTipInitialDelay = ttm.getInitialDelay();
341: toolTipReshowDelay = ttm.getReshowDelay();
342: ttm.setInitialDelay(200);
343: ttm.setReshowDelay(0);
344: super.processMouseEvent(evt);
345: break;
346: case MouseEvent.MOUSE_EXITED:
347: ttm.setInitialDelay(toolTipInitialDelay);
348: ttm.setReshowDelay(toolTipReshowDelay);
349: super.processMouseEvent(evt);
350: break; */
351: case MouseEvent.MOUSE_CLICKED:
352: TreePath path = getPathForLocation(evt.getX(), evt
353: .getY());
354: if (path != null) {
355: if (!isPathSelected(path))
356: setSelectionPath(path);
357:
358: Object obj = ((DefaultMutableTreeNode) path
359: .getLastPathComponent()).getUserObject();
360: if (!(obj instanceof HelpNode)) {
361: this .expandPath(path);
362: return;
363: }
364:
365: HelpNode node = (HelpNode) obj;
366:
367: helpViewer.gotoURL(node.href, true, 0);
368: }
369:
370: super .processMouseEvent(evt);
371: break;
372: default:
373: super .processMouseEvent(evt);
374: break;
375: }
376: } //}}}
377:
378: //{{{ cellRectIsVisible() method
379: private boolean cellRectIsVisible(Rectangle cellRect) {
380: Rectangle vr = TOCTree.this .getVisibleRect();
381: return vr.contains(cellRect.x, cellRect.y)
382: && vr.contains(cellRect.x + cellRect.width,
383: cellRect.y + cellRect.height);
384: } //}}}
385: } //}}}
386:
387: //{{{ TOCCellRenderer class
388: class TOCCellRenderer extends DefaultTreeCellRenderer {
389: EmptyBorder border = new EmptyBorder(1, 0, 1, 1);
390:
391: public Component getTreeCellRendererComponent(JTree tree,
392: Object value, boolean sel, boolean expanded,
393: boolean leaf, int row, boolean focus) {
394: super .getTreeCellRendererComponent(tree, value, sel,
395: expanded, leaf, row, focus);
396: setIcon(leaf ? FileCellRenderer.fileIcon
397: : (expanded ? FileCellRenderer.openDirIcon
398: : FileCellRenderer.dirIcon));
399: setBorder(border);
400:
401: return this ;
402: }
403: } //}}}
404:
405: //{{{ PluginCompare class
406: static class PluginCompare implements Comparator {
407: public int compare(Object o1, Object o2) {
408: EditPlugin p1 = (EditPlugin) o1;
409: EditPlugin p2 = (EditPlugin) o2;
410: return StandardUtilities.compareStrings(jEdit
411: .getProperty("plugin." + p1.getClassName()
412: + ".name"), jEdit.getProperty("plugin."
413: + p2.getClassName() + ".name"), true);
414: }
415: } //}}}
416: }
|