Source code: org/gjt/sp/jedit/help/HelpTOCPanel.java
1 /*
2 * HelpTOCPanel.java - Help table of contents
3 * :tabSize=8:indentSize=8:noTabs=false:
4 * :folding=explicit:collapseFolds=1:
5 *
6 * Copyright (C) 1999, 2000, 2001, 2002 Slava Pestov
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23 package org.gjt.sp.jedit.help;
24
25 //{{{ Imports
26 import com.microstar.xml.*;
27 import javax.swing.*;
28 import javax.swing.border.*;
29 import javax.swing.tree.*;
30 import java.awt.*;
31 import java.awt.event.*;
32 import java.io.*;
33 import java.net.*;
34 import java.util.*;
35 import org.gjt.sp.jedit.browser.FileCellRenderer; // for icons
36 import org.gjt.sp.jedit.io.VFSManager;
37 import org.gjt.sp.jedit.*;
38 import org.gjt.sp.util.Log;
39 //}}}
40
41 class HelpTOCPanel extends JPanel
42 {
43 //{{{ HelpTOCPanel constructor
44 HelpTOCPanel(HelpViewer helpViewer)
45 {
46 super(new BorderLayout());
47
48 this.helpViewer = helpViewer;
49 nodes = new Hashtable();
50
51 toc = new TOCTree();
52
53 // looks bad with the OS X L&F, apparently...
54 if(!OperatingSystem.isMacOSLF())
55 toc.putClientProperty("JTree.lineStyle", "Angled");
56
57 toc.setCellRenderer(new TOCCellRenderer());
58 toc.setEditable(false);
59 toc.setShowsRootHandles(true);
60
61 add(BorderLayout.CENTER,new JScrollPane(toc));
62
63 load();
64 } //}}}
65
66 //{{{ selectNode() method
67 void selectNode(String shortURL)
68 {
69 if(tocModel == null)
70 return;
71
72 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodes.get(shortURL);
73
74 if(node == null)
75 return;
76
77 TreePath path = new TreePath(tocModel.getPathToRoot(node));
78 toc.expandPath(path);
79 toc.setSelectionPath(path);
80 toc.scrollPathToVisible(path);
81 } //}}}
82
83 //{{{ load() method
84 void load()
85 {
86 DefaultTreeModel empty = new DefaultTreeModel(
87 new DefaultMutableTreeNode(
88 jEdit.getProperty("helpviewer.toc.loading")));
89 toc.setModel(empty);
90 toc.setRootVisible(true);
91
92 VFSManager.runInWorkThread(new Runnable()
93 {
94 public void run()
95 {
96 createTOC();
97 tocModel.reload(tocRoot);
98 toc.setModel(tocModel);
99 toc.setRootVisible(false);
100 for(int i = 0; i <tocRoot.getChildCount(); i++)
101 {
102 DefaultMutableTreeNode node =
103 (DefaultMutableTreeNode)
104 tocRoot.getChildAt(i);
105 toc.expandPath(new TreePath(
106 node.getPath()));
107 }
108 if(helpViewer.getShortURL() != null)
109 selectNode(helpViewer.getShortURL());
110 }
111 });
112 } //}}}
113
114 //{{{ Private members
115 private HelpViewer 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 {
124 DefaultMutableTreeNode node = new DefaultMutableTreeNode(
125 new HelpNode(href,title),true);
126 nodes.put(href,node);
127 return node;
128 } //}}}
129
130 //{{{ createTOC() method
131 private void createTOC()
132 {
133 EditPlugin[] plugins = jEdit.getPlugins();
134 Arrays.sort(plugins,new PluginCompare());
135 tocRoot = new DefaultMutableTreeNode();
136
137 tocRoot.add(createNode("welcome.html",
138 jEdit.getProperty("helpviewer.toc.welcome")));
139
140 tocRoot.add(createNode("README.txt",
141 jEdit.getProperty("helpviewer.toc.readme")));
142 tocRoot.add(createNode("CHANGES.txt",
143 jEdit.getProperty("helpviewer.toc.changes")));
144 tocRoot.add(createNode("TODO.txt",
145 jEdit.getProperty("helpviewer.toc.todo")));
146 tocRoot.add(createNode("COPYING.txt",
147 jEdit.getProperty("helpviewer.toc.copying")));
148 tocRoot.add(createNode("COPYING.DOC.txt",
149 jEdit.getProperty("helpviewer.toc.copying-doc")));
150 tocRoot.add(createNode("Apache.LICENSE.txt",
151 jEdit.getProperty("helpviewer.toc.copying-apache")));
152
153 loadTOC(tocRoot,"news42/toc.xml");
154 loadTOC(tocRoot,"users-guide/toc.xml");
155 loadTOC(tocRoot,"FAQ/toc.xml");
156 loadTOC(tocRoot,"api/toc.xml");
157
158 DefaultMutableTreeNode pluginTree = new DefaultMutableTreeNode(
159 jEdit.getProperty("helpviewer.toc.plugins"),true);
160
161 for(int i = 0; i < plugins.length; i++)
162 {
163 EditPlugin plugin = plugins[i];
164
165 String name = plugin.getClassName();
166
167 String docs = jEdit.getProperty("plugin." + name + ".docs");
168 String label = jEdit.getProperty("plugin." + name + ".name");
169 if(docs != null)
170 {
171 if(label != null && docs != null)
172 {
173 String path = plugin.getPluginJAR()
174 .getClassLoader()
175 .getResourceAsPath(docs);
176 pluginTree.add(createNode(
177 path,label));
178 }
179 }
180 }
181
182 if(pluginTree.getChildCount() != 0)
183 tocRoot.add(pluginTree);
184 else
185 {
186 // so that HelpViewer constructor doesn't try to expand
187 pluginTree = null;
188 }
189
190 tocModel = new DefaultTreeModel(tocRoot);
191 } //}}}
192
193 //{{{ loadTOC() method
194 private void loadTOC(DefaultMutableTreeNode root, String path)
195 {
196 TOCHandler h = new TOCHandler(root,MiscUtilities.getParentOfPath(path));
197 XmlParser parser = new XmlParser();
198 Reader in = null;
199 parser.setHandler(h);
200
201 try
202 {
203 in = new InputStreamReader(
204 new URL(helpViewer.getBaseURL()
205 + '/' + path).openStream());
206 parser.parse(null, null, in);
207 }
208 catch(XmlException xe)
209 {
210 int line = xe.getLine();
211 String message = xe.getMessage();
212 Log.log(Log.ERROR,this,path + ':' + line
213 + ": " + message);
214 }
215 catch(Exception e)
216 {
217 Log.log(Log.ERROR,this,e);
218 }
219 finally
220 {
221 try
222 {
223 if(in != null)
224 in.close();
225 }
226 catch(IOException io)
227 {
228 Log.log(Log.ERROR,this,io);
229 }
230 }
231 } //}}}
232
233 //}}}
234
235 //{{{ HelpNode class
236 static class HelpNode
237 {
238 String href, title;
239
240 //{{{ HelpNode constructor
241 HelpNode(String href, String title)
242 {
243 this.href = href;
244 this.title = title;
245 } //}}}
246
247 //{{{ toString() method
248 public String toString()
249 {
250 return title;
251 } //}}}
252 } //}}}
253
254 //{{{ TOCHandler class
255 class TOCHandler extends HandlerBase
256 {
257 String dir;
258
259 //{{{ TOCHandler constructor
260 TOCHandler(DefaultMutableTreeNode root, String dir)
261 {
262 nodes = new Stack();
263 node = root;
264 this.dir = dir;
265 } //}}}
266
267 //{{{ attribute() method
268 public void attribute(String aname, String value, boolean isSpecified)
269 {
270 if(aname.equals("HREF"))
271 href = value;
272 } //}}}
273
274 //{{{ charData() method
275 public void charData(char[] c, int off, int len)
276 {
277 if(tag.equals("TITLE"))
278 {
279 StringBuffer buf = new StringBuffer();
280 for(int i = 0; i < len; i++)
281 {
282 char ch = c[off + i];
283 if(ch == ' ' || !Character.isWhitespace(ch))
284 buf.append(ch);
285 }
286 title = buf.toString();
287 }
288 } //}}}
289
290 //{{{ startElement() method
291 public void startElement(String name)
292 {
293 tag = name;
294 } //}}}
295
296 //{{{ endElement() method
297 public void endElement(String name)
298 {
299 if(name == null)
300 return;
301
302 if(name.equals("TITLE"))
303 {
304 DefaultMutableTreeNode newNode = createNode(
305 dir + href,title);
306 node.add(newNode);
307 nodes.push(node);
308 node = newNode;
309 }
310 else if(name.equals("ENTRY"))
311 node = (DefaultMutableTreeNode)nodes.pop();
312 } //}}}
313
314 //{{{ Private members
315 private String tag;
316 private String title;
317 private String href;
318 private DefaultMutableTreeNode node;
319 private Stack nodes;
320 //}}}
321 } //}}}
322
323 //{{{ TOCTree class
324 class TOCTree extends JTree
325 {
326 //{{{ TOCTree constructor
327 TOCTree()
328 {
329 ToolTipManager.sharedInstance().registerComponent(this);
330 } //}}}
331
332 //{{{ getToolTipText() method
333 public final String getToolTipText(MouseEvent evt)
334 {
335 TreePath path = getPathForLocation(evt.getX(), evt.getY());
336 if(path != null)
337 {
338 Rectangle cellRect = getPathBounds(path);
339 if(cellRect != null && !cellRectIsVisible(cellRect))
340 return path.getLastPathComponent().toString();
341 }
342 return null;
343 } //}}}
344
345 //{{{ getToolTipLocation() method
346 /* public final Point getToolTipLocation(MouseEvent evt)
347 {
348 TreePath path = getPathForLocation(evt.getX(), evt.getY());
349 if(path != null)
350 {
351 Rectangle cellRect = getPathBounds(path);
352 if(cellRect != null && !cellRectIsVisible(cellRect))
353 {
354 return new Point(cellRect.x + 14, cellRect.y);
355 }
356 }
357 return null;
358 } */ //}}}
359
360 //{{{ processMouseEvent() method
361 protected void processMouseEvent(MouseEvent evt)
362 {
363 //ToolTipManager ttm = ToolTipManager.sharedInstance();
364
365 switch(evt.getID())
366 {
367 /* case MouseEvent.MOUSE_ENTERED:
368 toolTipInitialDelay = ttm.getInitialDelay();
369 toolTipReshowDelay = ttm.getReshowDelay();
370 ttm.setInitialDelay(200);
371 ttm.setReshowDelay(0);
372 super.processMouseEvent(evt);
373 break;
374 case MouseEvent.MOUSE_EXITED:
375 ttm.setInitialDelay(toolTipInitialDelay);
376 ttm.setReshowDelay(toolTipReshowDelay);
377 super.processMouseEvent(evt);
378 break; */
379 case MouseEvent.MOUSE_CLICKED:
380 TreePath path = getPathForLocation(evt.getX(),evt.getY());
381 if(path != null)
382 {
383 if(!isPathSelected(path))
384 setSelectionPath(path);
385
386 Object obj = ((DefaultMutableTreeNode)
387 path.getLastPathComponent())
388 .getUserObject();
389 if(!(obj instanceof HelpNode))
390 {
391 this.expandPath(path);
392 return;
393 }
394
395 HelpNode node = (HelpNode)obj;
396
397 helpViewer.gotoURL(node.href,true);
398 }
399
400 super.processMouseEvent(evt);
401 break;
402 default:
403 super.processMouseEvent(evt);
404 break;
405 }
406 } //}}}
407
408 //{{{ Private members
409 private int toolTipInitialDelay = -1;
410 private int toolTipReshowDelay = -1;
411
412 //{{{ cellRectIsVisible() method
413 private boolean cellRectIsVisible(Rectangle cellRect)
414 {
415 Rectangle vr = TOCTree.this.getVisibleRect();
416 return vr.contains(cellRect.x,cellRect.y) &&
417 vr.contains(cellRect.x + cellRect.width,
418 cellRect.y + cellRect.height);
419 } //}}}
420
421 //}}}
422 } //}}}
423
424 //{{{ TOCCellRenderer class
425 class TOCCellRenderer extends DefaultTreeCellRenderer
426 {
427 EmptyBorder border = new EmptyBorder(1,0,1,1);
428
429 public Component getTreeCellRendererComponent(JTree tree,
430 Object value, boolean sel, boolean expanded,
431 boolean leaf, int row, boolean focus)
432 {
433 super.getTreeCellRendererComponent(tree,value,sel,
434 expanded,leaf,row,focus);
435 setIcon(leaf ? FileCellRenderer.fileIcon
436 : (expanded ? FileCellRenderer.openDirIcon
437 : FileCellRenderer.dirIcon));
438 setBorder(border);
439
440 return this;
441 }
442 } //}}}
443
444 //{{{ PluginCompare class
445 static class PluginCompare implements Comparator
446 {
447 public int compare(Object o1, Object o2)
448 {
449 EditPlugin p1 = (EditPlugin)o1;
450 EditPlugin p2 = (EditPlugin)o2;
451 return MiscUtilities.compareStrings(
452 jEdit.getProperty("plugin." + p1.getClassName() + ".name"),
453 jEdit.getProperty("plugin." + p2.getClassName() + ".name"),
454 true);
455 }
456 } //}}}
457 }