PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

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