PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

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