PageRenderTime 175ms CodeModel.GetById 122ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 0ms

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

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