PageRenderTime 149ms CodeModel.GetById 41ms app.highlight 81ms RepoModel.GetById 19ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/gui/HelpViewer.java

#
Java | 636 lines | 466 code | 81 blank | 89 comment | 66 complexity | 3f1517361aaa4f3545e86aa062f3fa11 MD5 | raw file
  1/*
  2 * HelpViewer.java - HTML Help viewer
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999, 2000, 2001 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
 23package org.gjt.sp.jedit.gui;
 24
 25//{{{ Imports
 26import com.microstar.xml.*;
 27import javax.swing.*;
 28import javax.swing.border.*;
 29import javax.swing.event.*;
 30import javax.swing.text.html.*;
 31import javax.swing.text.Document;
 32import javax.swing.tree.*;
 33import java.awt.*;
 34import java.awt.event.*;
 35import java.io.*;
 36import java.net.*;
 37import java.util.*;
 38import org.gjt.sp.jedit.browser.FileCellRenderer; // for icons
 39import org.gjt.sp.jedit.msg.PropertiesChanged;
 40import org.gjt.sp.jedit.*;
 41import org.gjt.sp.util.Log;
 42//}}}
 43
 44/**
 45 * jEdit's HTML viewer. It uses a Swing JEditorPane to display the HTML,
 46 * and implements a URL history.
 47 * @author Slava Pestov
 48 * @version $Id: HelpViewer.java 3936 2001-12-21 07:02:14Z spestov $
 49 */
 50public class HelpViewer extends JFrame implements EBComponent
 51{
 52	//{{{ HelpViewer constructor
 53	/**
 54	 * Creates a new help viewer for the specified URL.
 55	 * @param url The URL
 56	 */
 57	public HelpViewer(URL url)
 58	{
 59		this(url.toString());
 60	} //}}}
 61
 62	//{{{ HelpViewer constructor
 63	/**
 64	 * Creates a new help viewer for the specified URL.
 65	 * @param url The URL
 66	 */
 67	public HelpViewer(String url)
 68	{
 69		super(jEdit.getProperty("helpviewer.title"));
 70
 71		setIconImage(GUIUtilities.getEditorIcon());
 72
 73		history = new String[25];
 74		nodes = new Hashtable();
 75
 76		ActionHandler actionListener = new ActionHandler();
 77
 78		JToolBar toolBar = new JToolBar();
 79		toolBar.setFloatable(false);
 80		toolBar.putClientProperty("JToolBar.isRollover",Boolean.TRUE);
 81
 82		JLabel label = new JLabel(jEdit.getProperty("helpviewer.url"));
 83		label.setBorder(new EmptyBorder(0,12,0,12));
 84		toolBar.add(label);
 85		Box box = new Box(BoxLayout.Y_AXIS);
 86		box.add(Box.createGlue());
 87		urlField = new JTextField();
 88		urlField.addKeyListener(new KeyHandler());
 89		Dimension dim = urlField.getPreferredSize();
 90		dim.width = Integer.MAX_VALUE;
 91		urlField.setMaximumSize(dim);
 92		box.add(urlField);
 93		box.add(Box.createGlue());
 94		toolBar.add(box);
 95
 96		toolBar.add(Box.createHorizontalStrut(6));
 97
 98		JPanel buttons = new JPanel();
 99		buttons.setLayout(new BoxLayout(buttons,BoxLayout.X_AXIS));
100		buttons.setBorder(new EmptyBorder(0,12,0,0));
101		back = new JButton(GUIUtilities.loadIcon("Back24.gif"));
102		back.setToolTipText(jEdit.getProperty("helpviewer.back"));
103		back.addActionListener(actionListener);
104		back.setRequestFocusEnabled(false);
105		toolBar.add(back);
106		forward = new JButton(GUIUtilities.loadIcon("Forward24.gif"));
107		forward.addActionListener(actionListener);
108		forward.setToolTipText(jEdit.getProperty("helpviewer.forward"));
109		forward.setRequestFocusEnabled(false);
110		toolBar.add(forward);
111		back.setPreferredSize(forward.getPreferredSize());
112
113		getContentPane().add(BorderLayout.NORTH,toolBar);
114
115		createTOC();
116
117		toc = new TOCTree(tocModel);
118		toc.putClientProperty("JTree.lineStyle", "Angled");
119		toc.setCellRenderer(new TOCCellRenderer());
120		toc.setEditable(false);
121		toc.setRootVisible(false);
122		toc.setShowsRootHandles(true);
123
124		DefaultMutableTreeNode node = (DefaultMutableTreeNode)
125			nodes.get("jeditresource:/doc/users-guide/using-jedit-part.html");
126		if(node != null)
127			toc.expandPath(new TreePath(node.getPath()));
128
129		viewer = new JEditorPane();
130		viewer.setEditable(false);
131		viewer.addHyperlinkListener(new LinkHandler());
132		viewer.setFont(new Font("Monospaced",Font.PLAIN,12));
133
134		final JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
135			new JScrollPane(toc),new JScrollPane(viewer));
136		splitter.setBorder(null);
137
138		getContentPane().add(BorderLayout.CENTER,splitter);
139
140		gotoURL(url,true);
141
142		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
143
144		getRootPane().setPreferredSize(new Dimension(800,500));
145
146		pack();
147		GUIUtilities.loadGeometry(this,"helpviewer");
148
149		EditBus.addToBus(this);
150
151		show();
152
153		SwingUtilities.invokeLater(new Runnable()
154		{
155			public void run()
156			{
157				splitter.setDividerLocation(250);
158			}
159		});
160	} //}}}
161
162	//{{{ gotoURL() method
163	/**
164	 * Displays the specified URL in the HTML component.
165	 * @param url The URL
166	 * @param addToHistory Should the URL be added to the back/forward
167	 * history?
168	 */
169	public void gotoURL(String url, boolean addToHistory)
170	{
171		// reset default cursor so that the hand cursor doesn't
172		// stick around
173		viewer.setCursor(Cursor.getDefaultCursor());
174
175		int index = url.indexOf('#');
176
177		URL _url = null;
178		try
179		{
180			_url = new URL(url);
181
182			urlField.setText(_url.toString());
183			viewer.setPage(_url);
184			if(addToHistory)
185			{
186				history[historyPos] = url;
187				if(historyPos + 1 == history.length)
188				{
189					System.arraycopy(history,1,history,
190						0,history.length - 1);
191					history[historyPos] = null;
192				}
193				else
194					historyPos++;
195			}
196		}
197		catch(MalformedURLException mf)
198		{
199			Log.log(Log.ERROR,this,mf);
200			String[] args = { url, mf.getMessage() };
201			GUIUtilities.error(this,"badurl",args);
202			return;
203		}
204		catch(IOException io)
205		{
206			Log.log(Log.ERROR,this,io);
207			String[] args = { url, io.toString() };
208			GUIUtilities.error(this,"read-error",args);
209			return;
210		}
211
212		// select the appropriate tree node.
213		DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodes.get(url);
214
215		if(node == null)
216			return;
217
218		TreePath path = new TreePath(tocModel.getPathToRoot(node));
219		toc.expandPath(path);
220		toc.setSelectionPath(path);
221		toc.scrollPathToVisible(path);
222	} //}}}
223
224	//{{{ dispose() method
225	public void dispose()
226	{
227		EditBus.removeFromBus(this);
228		GUIUtilities.saveGeometry(this,"helpviewer");
229		super.dispose();
230	} //}}}
231
232	//{{{ handleMessage() method
233	public void handleMessage(EBMessage msg)
234	{
235		if(msg instanceof PropertiesChanged)
236			SwingUtilities.updateComponentTreeUI(getRootPane());
237	} //}}}
238
239	//{{{ Private members
240
241	//{{{ Instance variables
242	private JButton back;
243	private JButton forward;
244	private DefaultTreeModel tocModel;
245	private JTree toc;
246	// this makes gotoURL()'s tree updating simpler
247	private Hashtable nodes;
248	private JEditorPane viewer;
249	private JTextField urlField;
250	private String[] history;
251	private int historyPos;
252	//}}}
253
254	//{{{ createTOC() method
255	private void createTOC()
256	{
257		DefaultMutableTreeNode root = new DefaultMutableTreeNode();
258
259		root.add(createNode("jeditresource:/doc/welcome.html",
260			jEdit.getProperty("helpviewer.toc.welcome")));
261
262		root.add(createNode("jeditresource:/doc/README.txt",
263			jEdit.getProperty("helpviewer.toc.readme")));
264		root.add(createNode("jeditresource:/doc/NEWS.txt",
265			jEdit.getProperty("helpviewer.toc.news")));
266		root.add(createNode("jeditresource:/doc/TODO.txt",
267			jEdit.getProperty("helpviewer.toc.todo")));
268		root.add(createNode("jeditresource:/doc/CHANGES.txt",
269			jEdit.getProperty("helpviewer.toc.changes")));
270		root.add(createNode("jeditresource:/doc/COPYING.txt",
271			jEdit.getProperty("helpviewer.toc.copying")));
272		root.add(createNode("jeditresource:/doc/COPYING.DOC.txt",
273			jEdit.getProperty("helpviewer.toc.copying-doc")));
274
275		loadUserGuideTOC(root);
276
277		DefaultMutableTreeNode pluginDocs = new DefaultMutableTreeNode(
278			jEdit.getProperty("helpviewer.toc.plugins"),true);
279
280		EditPlugin[] plugins = jEdit.getPlugins();
281		for(int i = 0; i < plugins.length; i++)
282		{
283			EditPlugin plugin = plugins[i];
284			EditPlugin.JAR jar = plugin.getJAR();
285			if(jar == null)
286				continue;
287
288			String name = plugin.getClassName();
289
290			String docs = jEdit.getProperty("plugin." + name + ".docs");
291			String label = jEdit.getProperty("plugin." + name + ".name");
292			if(docs != null)
293			{
294				if(label != null && docs != null)
295				{
296					URL url = jar.getClassLoader()
297						.getResource(docs);
298					if(url != null)
299					{
300						pluginDocs.add(createNode(
301							url.toString(),label));
302					}
303				}
304			}
305		}
306
307		if(pluginDocs.getChildCount() != 0)
308			root.add(pluginDocs);
309
310		tocModel = new DefaultTreeModel(root);
311	} //}}}
312
313	//{{{ loadUserGuideTOC() method
314	private void loadUserGuideTOC(DefaultMutableTreeNode root)
315	{
316		URL resource = getClass().getResource("/doc/users-guide/toc.xml");
317		if(resource == null)
318			return;
319
320		TOCHandler h = new TOCHandler(root);
321		XmlParser parser = new XmlParser();
322		parser.setHandler(h);
323
324		try
325		{
326			// use a URL here because with Web Start version,
327			// toc.xml is not a local file
328			parser.parse(null, null, new InputStreamReader(
329				resource.openStream()));
330		}
331		catch(XmlException xe)
332		{
333			int line = xe.getLine();
334			String message = xe.getMessage();
335			Log.log(Log.ERROR,this,"toc.xml:" + line
336				+ ": " + message);
337		}
338		catch(Exception e)
339		{
340			Log.log(Log.ERROR,this,e);
341		}
342	} //}}}
343
344	//{{{ createNode() method
345	private DefaultMutableTreeNode createNode(String href, String title)
346	{
347		DefaultMutableTreeNode node = new DefaultMutableTreeNode(
348			new HelpNode(href,title),true);
349		nodes.put(href,node);
350		return node;
351	} //}}}
352
353	//}}}
354
355	//{{{ Inner classes
356
357	//{{{ HelpNode class
358	static class HelpNode
359	{
360		String href, title;
361
362		//{{{ HelpNode constructor
363		HelpNode(String href, String title)
364		{
365			this.href = href;
366			this.title = title;
367		} //}}}
368
369		//{{{ toString() method
370		public String toString()
371		{
372			return title;
373		} //}}}
374	} //}}}
375
376	//{{{ TOCHandler class
377	class TOCHandler extends HandlerBase
378	{
379		//{{{ TOCHandler constructor
380		TOCHandler(DefaultMutableTreeNode root)
381		{
382			nodes = new Stack();
383			node = root;
384		} //}}}
385
386		//{{{ attribute() method
387		public void attribute(String aname, String value, boolean isSpecified)
388		{
389			if(aname.equals("HREF"))
390				href = value;
391		} //}}}
392
393		//{{{ charData() method
394		public void charData(char[] c, int off, int len)
395		{
396			if(tag.equals("TITLE"))
397				title = new String(c, off, len);
398		} //}}}
399
400		//{{{ startElement() method
401		public void startElement(String name)
402		{
403			tag = name;
404		} //}}}
405
406		//{{{ endElement() method
407		public void endElement(String name)
408		{
409			if(name == null)
410				return;
411
412			if(name.equals("TITLE"))
413			{
414				DefaultMutableTreeNode newNode = createNode(
415					"jeditresource:/doc/users-guide/"
416					+ href,title);
417				node.add(newNode);
418				nodes.push(node);
419				node = newNode;
420			}
421			else if(name.equals("ENTRY"))
422				node = (DefaultMutableTreeNode)nodes.pop();
423		} //}}}
424
425		//{{{ Private members
426		private String tag;
427		private String title;
428		private String href;
429		private DefaultMutableTreeNode node;
430		private Stack nodes;
431		//}}}
432	} //}}}
433
434	//{{{ TOCTree class
435	class TOCTree extends JTree
436	{
437		//{{{ TOCTree constructor
438		TOCTree(TreeModel model)
439		{
440			super(model);
441			ToolTipManager.sharedInstance().registerComponent(this);
442		} //}}}
443
444		//{{{ getToolTipText() method
445		public final String getToolTipText(MouseEvent evt)
446		{
447			TreePath path = getPathForLocation(evt.getX(), evt.getY());
448			if(path != null)
449			{
450				Rectangle cellRect = getPathBounds(path);
451				if(cellRect != null && !cellRectIsVisible(cellRect))
452					return path.getLastPathComponent().toString();
453			}
454			return null;
455		} //}}}
456
457		//{{{ getToolTipLocation() method
458		public final Point getToolTipLocation(MouseEvent evt)
459		{
460			TreePath path = getPathForLocation(evt.getX(), evt.getY());
461			if(path != null)
462			{
463				Rectangle cellRect = getPathBounds(path);
464				if(cellRect != null && !cellRectIsVisible(cellRect))
465				{
466					return new Point(cellRect.x + 17, cellRect.y - 1);
467				}
468			}
469			return null;
470		} //}}}
471
472		//{{{ processMouseEvent() method
473		protected void processMouseEvent(MouseEvent evt)
474		{
475			ToolTipManager ttm = ToolTipManager.sharedInstance();
476
477			switch(evt.getID())
478			{
479			case MouseEvent.MOUSE_ENTERED:
480				toolTipInitialDelay = ttm.getInitialDelay();
481				toolTipReshowDelay = ttm.getReshowDelay();
482				ttm.setInitialDelay(200);
483				ttm.setReshowDelay(0);
484				super.processMouseEvent(evt);
485				break;
486			case MouseEvent.MOUSE_EXITED:
487				ttm.setInitialDelay(toolTipInitialDelay);
488				ttm.setReshowDelay(toolTipReshowDelay);
489				super.processMouseEvent(evt);
490				break;
491			case MouseEvent.MOUSE_CLICKED:
492				TreePath path = getPathForLocation(evt.getX(),evt.getY());
493				if(path != null)
494				{
495					if(!isPathSelected(path))
496						setSelectionPath(path);
497
498					Object obj = ((DefaultMutableTreeNode)
499						path.getLastPathComponent())
500						.getUserObject();
501					if(!(obj instanceof HelpNode))
502					{
503						toc.expandPath(path);
504						return;
505					}
506
507					HelpNode node = (HelpNode)obj;
508
509					gotoURL(node.href,true);
510				}
511
512				super.processMouseEvent(evt);
513				break;
514			default:
515				super.processMouseEvent(evt);
516				break;
517			}
518		} //}}}
519
520		//{{{ Private members
521		private int toolTipInitialDelay = -1;
522		private int toolTipReshowDelay = -1;
523
524		//{{{ cellRectIsVisible() method
525		private boolean cellRectIsVisible(Rectangle cellRect)
526		{
527			Rectangle vr = TOCTree.this.getVisibleRect();
528			return vr.contains(cellRect.x,cellRect.y) &&
529				vr.contains(cellRect.x + cellRect.width,
530				cellRect.y + cellRect.height);
531		} //}}}
532
533		//}}}
534	} //}}}
535
536	//{{{ TOCCellRenderer class
537	class TOCCellRenderer extends DefaultTreeCellRenderer
538	{
539		EmptyBorder border = new EmptyBorder(1,0,1,1);
540
541		public Component getTreeCellRendererComponent(JTree tree,
542			Object value, boolean sel, boolean expanded,
543			boolean leaf, int row, boolean focus)
544		{
545			super.getTreeCellRendererComponent(tree,value,sel,
546				expanded,leaf,row,focus);
547			setIcon(leaf ? FileCellRenderer.fileIcon
548				: (expanded ? FileCellRenderer.openDirIcon
549				: FileCellRenderer.dirIcon));
550			setBorder(border);
551
552			return this;
553		}
554	} //}}}
555
556	//{{{ ActionHandler class
557	class ActionHandler implements ActionListener
558	{
559		//{{{ actionPerformed() class
560		public void actionPerformed(ActionEvent evt)
561		{
562			Object source = evt.getSource();
563			if(source == back)
564			{
565				if(historyPos <= 1)
566					getToolkit().beep();
567				else
568				{
569					String url = history[--historyPos - 1];
570					gotoURL(url,false);
571				}
572			}
573			else if(source == forward)
574			{
575				if(history.length - historyPos <= 1)
576					getToolkit().beep();
577				else
578				{
579					String url = history[historyPos];
580					if(url == null)
581						getToolkit().beep();
582					else
583					{
584						historyPos++;
585						gotoURL(url,false);
586					}
587				}
588			}
589		} //}}}
590	} //}}}
591
592	//{{{ LinkHandler class
593	class LinkHandler implements HyperlinkListener
594	{
595		//{{{ hyperlinkUpdate() method
596		public void hyperlinkUpdate(HyperlinkEvent evt)
597		{
598			if(evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
599			{
600				if(evt instanceof HTMLFrameHyperlinkEvent)
601				{
602					((HTMLDocument)viewer.getDocument())
603						.processHTMLFrameHyperlinkEvent(
604						(HTMLFrameHyperlinkEvent)evt);
605				}
606				else
607				{
608					URL url = evt.getURL();
609					if(url != null)
610						gotoURL(url.toString(),true);
611				}
612			}
613			else if (evt.getEventType() == HyperlinkEvent.EventType.ENTERED) {
614				viewer.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
615			}
616			else if (evt.getEventType() == HyperlinkEvent.EventType.EXITED) {
617				viewer.setCursor(Cursor.getDefaultCursor());
618			}
619		} //}}}
620	} //}}}
621
622	//{{{ KeyHandler class
623	class KeyHandler extends KeyAdapter
624	{
625		//{{{ keyPressed() method
626		public void keyPressed(KeyEvent evt)
627		{
628			if(evt.getKeyCode() == KeyEvent.VK_ENTER)
629			{
630				gotoURL(urlField.getText(),true);
631			}
632		} //}}}
633	} //}}}
634
635	//}}}
636}