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

/bundles/plugins-trunk/InfoViewer/infoviewer/InfoViewer.java

#
Java | 1412 lines | 998 code | 191 blank | 223 comment | 133 complexity | 54a053dd92c8b7de9168bfd2fd434b1f 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. * InfoViewer.java - Info viewer for HTML, txt
  3. * Copyright (C) 2000-2002 Dirk Moebius
  4. * Based on HTMLViewer.java Copyright (C) 1999 Slava Pestov
  5. *
  6. * :tabSize=4:indentSize=4:noTabs=true:maxLineLen=0:
  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 infoviewer;
  23. import infoviewer.actions.InfoViewerAction;
  24. import infoviewer.actions.ToggleSidebar;
  25. import infoviewer.workaround.EnhancedJEditorPane;
  26. import infoviewer.workaround.EnhancedJToolBar;
  27. import java.awt.BorderLayout;
  28. import java.awt.Cursor;
  29. import java.awt.Dimension;
  30. import java.awt.Font;
  31. import java.awt.GridLayout;
  32. import java.awt.Image;
  33. import java.awt.event.ActionEvent;
  34. import java.awt.event.ActionListener;
  35. import java.awt.event.InputEvent;
  36. import java.awt.event.KeyAdapter;
  37. import java.awt.event.KeyEvent;
  38. import java.awt.event.MouseAdapter;
  39. import java.awt.event.MouseEvent;
  40. import java.beans.PropertyChangeEvent;
  41. import java.beans.PropertyChangeListener;
  42. import java.io.File;
  43. import java.io.FileNotFoundException;
  44. import java.io.IOException;
  45. import java.net.MalformedURLException;
  46. import java.net.URI;
  47. import java.net.URL;
  48. import java.util.Arrays;
  49. import java.util.Enumeration;
  50. import javax.accessibility.AccessibleHypertext;
  51. import javax.accessibility.AccessibleText;
  52. import javax.swing.Box;
  53. import javax.swing.ImageIcon;
  54. import javax.swing.JButton;
  55. import javax.swing.JComponent;
  56. import javax.swing.JEditorPane;
  57. import javax.swing.JLabel;
  58. import javax.swing.JMenu;
  59. import javax.swing.JMenuBar;
  60. import javax.swing.JMenuItem;
  61. import javax.swing.JPanel;
  62. import javax.swing.JPopupMenu;
  63. import javax.swing.JRootPane;
  64. import javax.swing.JScrollBar;
  65. import javax.swing.JScrollPane;
  66. import javax.swing.JSeparator;
  67. import javax.swing.JToolBar;
  68. import javax.swing.SwingUtilities;
  69. import javax.swing.Timer;
  70. import javax.swing.border.BevelBorder;
  71. import javax.swing.event.HyperlinkEvent;
  72. import javax.swing.event.HyperlinkListener;
  73. import javax.swing.text.Caret;
  74. import javax.swing.text.Document;
  75. import javax.swing.text.Style;
  76. import javax.swing.text.html.HTMLDocument;
  77. import javax.swing.text.html.HTMLEditorKit;
  78. import javax.swing.text.html.HTMLFrameHyperlinkEvent;
  79. import javax.swing.text.html.StyleSheet;
  80. import org.gjt.sp.jedit.ActionContext;
  81. import org.gjt.sp.jedit.ActionSet;
  82. import org.gjt.sp.jedit.Buffer;
  83. import org.gjt.sp.jedit.EBComponent;
  84. import org.gjt.sp.jedit.EBMessage;
  85. import org.gjt.sp.jedit.EditBus;
  86. import org.gjt.sp.jedit.EditPane;
  87. import org.gjt.sp.jedit.GUIUtilities;
  88. import org.gjt.sp.jedit.MiscUtilities;
  89. import org.gjt.sp.jedit.View;
  90. import org.gjt.sp.jedit.gui.HistoryModel;
  91. import org.gjt.sp.jedit.jEdit;
  92. import org.gjt.sp.jedit.gui.DefaultFocusComponent;
  93. import org.gjt.sp.jedit.gui.DockableWindowManager;
  94. import org.gjt.sp.jedit.gui.HistoryTextField;
  95. import org.gjt.sp.jedit.io.FileVFS;
  96. import org.gjt.sp.jedit.msg.BufferUpdate;
  97. import org.gjt.sp.jedit.msg.EditPaneUpdate;
  98. import org.gjt.sp.jedit.msg.PropertiesChanged;
  99. import org.gjt.sp.util.Log;
  100. /**
  101. * an info viewer for jEdit. It uses a Swing JEditorPane to display the HTML,
  102. * and implements a URL history, bookmarks and some other web browsing
  103. * functions.
  104. *
  105. * @author Dirk Moebius
  106. * @author Slava Pestov
  107. */
  108. public class InfoViewer extends JPanel implements HyperlinkListener, PropertyChangeListener,
  109. EBComponent, DefaultFocusComponent
  110. {
  111. // {{{ Proteced Members
  112. protected JPanel outerPanel;
  113. protected JPanel innerPanel;
  114. protected MyScrollPane scrViewer;
  115. // }}}
  116. /**
  117. * Creates a new info viewer instance.
  118. *
  119. * @param view
  120. * where this dockable is docked into.
  121. * @param position
  122. * docking position.
  123. */
  124. public InfoViewer(View view, String position)
  125. {
  126. setName("infoviewer");
  127. if (position == null)
  128. position = DockableWindowManager.FLOATING;
  129. setLayout(new BorderLayout());
  130. this.view = view;
  131. this.isDocked = !(position.equals(DockableWindowManager.FLOATING));
  132. this.history = new History();
  133. this.historyhandler = new URLButtonHandler(false);
  134. this.bookmarkhandler = new URLButtonHandler(true);
  135. // initialize actions
  136. createActions();
  137. /** this is for handling the "esc" key only */
  138. KeyHandler escKeyHandler = new KeyHandler();
  139. addKeyListener(escKeyHandler);
  140. JRootPane root = getRootPane();
  141. if (root != null)
  142. root.addKeyListener(escKeyHandler);
  143. // the menu
  144. JMenuBar mb = createMenu();
  145. // the toolbar
  146. JToolBar tb = createToolbar();
  147. // the url address bar
  148. JPanel addressBar = createAddressBar();
  149. addressBar.addKeyListener(escKeyHandler);
  150. // the status bar
  151. JPanel statusBar = createStatusBar();
  152. // the viewer
  153. viewer = new EnhancedJEditorPane();
  154. viewer.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
  155. viewer.addKeyListener(escKeyHandler);
  156. viewer.setEditable(false);
  157. viewer.setFocusable(true);
  158. Font vf = jEdit.getFontProperty("metal.secondary.font");
  159. viewer.setFont(jEdit.getFontProperty("helpviewer.font", vf));
  160. viewer.addHyperlinkListener(this);
  161. viewer.addPropertyChangeListener(this);
  162. viewer.addMouseListener(new MouseHandler());
  163. scrViewer = new MyScrollPane(viewer);
  164. scrViewer.setFocusable(false);
  165. viewer.setArrowKeyHandler(new ArrowKeyHandler());
  166. // HTMLEditorKit is not yet in use here
  167. // the inner content: url textfield, viewer, status bar
  168. String appearancePrefix = "infoviewer.appearance."
  169. + (isDocked ? "docked." : "floating.");
  170. innerPanel = new JPanel(new BorderLayout());
  171. innerPanel.add(scrViewer, BorderLayout.CENTER);
  172. if (jEdit.getBooleanProperty(appearancePrefix + "showAddressbar"))
  173. innerPanel.add(addressBar, BorderLayout.NORTH);
  174. if (jEdit.getBooleanProperty(appearancePrefix + "showStatusbar"))
  175. innerPanel.add(statusBar, BorderLayout.SOUTH);
  176. // the outer content: toolbar, inner content
  177. outerPanel = new JPanel(new BorderLayout());
  178. outerPanel.add(innerPanel, BorderLayout.CENTER);
  179. if (jEdit.getBooleanProperty(appearancePrefix + "showToolbar"))
  180. outerPanel.add(tb, BorderLayout.NORTH);
  181. // overall layout: menu, outer content
  182. if (jEdit.getBooleanProperty(appearancePrefix + "showMenu"))
  183. add(mb, BorderLayout.NORTH);
  184. add(outerPanel, BorderLayout.CENTER);
  185. updateStatus();
  186. updateTimers();
  187. // show start URL (either homepage or current buffer)
  188. if (jEdit.getBooleanProperty("infoviewer.autoupdate")
  189. && (jEdit.getBooleanProperty("infoviewer.autoupdate.onSwitch")
  190. || jEdit.getBooleanProperty("infoviewer.autoupdate.onSave") || jEdit
  191. .getBooleanProperty("infoviewer.autoupdate.onChange")))
  192. {
  193. // auto-update and sync with buffer: open current buffer
  194. // at startup
  195. gotoBufferURL();
  196. }
  197. else
  198. {
  199. if (jEdit.getBooleanProperty("infoviewer.restorePreviousPage") && urlField.getModel().getSize() > 0) {
  200. // Restore previous page on startup
  201. gotoURL(urlField.getModel().getItem(0), true, 0);
  202. }
  203. else {
  204. // open homepage at startup
  205. String home = jEdit.getProperty("infoviewer.homepage");
  206. currentURL = new TitledURLEntry("Infoviewer Homepage", home);
  207. if (home != null)
  208. gotoURL(home, true, 0);
  209. }
  210. }
  211. urlField.addKeyListener(escKeyHandler);
  212. setFocusCycleRoot(true);
  213. Caret c = viewer.getCaret();
  214. c.setVisible(true);
  215. }
  216. protected Document getDocument()
  217. {
  218. return viewer.getDocument();
  219. }
  220. public TitledURLEntry getCurrentURL()
  221. {
  222. String url = urlField.getText();
  223. if (url == null || url.length() < 1)
  224. return null;
  225. currentURL = new TitledURLEntry(title.getText(), urlField.getText());
  226. int scrollBarPos = scrViewer.getVerticalScrollBar().getValue();
  227. currentURL.setScrollBarPos(scrollBarPos);
  228. return currentURL;
  229. }
  230. // {{{ gotoURL()
  231. public void gotoURL(String url)
  232. {
  233. gotoURL(url, true, 0);
  234. }
  235. public void gotoURL(String url, boolean addToHistory, int vertPos)
  236. {
  237. try
  238. {
  239. gotoURL(new URL(url), addToHistory, vertPos);
  240. }
  241. catch (MalformedURLException mfue)
  242. {
  243. }
  244. }
  245. /**
  246. * Displays the specified URL in the HTML component.
  247. *
  248. * @param url
  249. * The URL as String
  250. * @param addToHistory
  251. * Should the URL be added to the back/forward history?
  252. */
  253. public void gotoURL(TitledURLEntry entry, boolean addToHistory)
  254. {
  255. String url = entry.getURL();
  256. try
  257. {
  258. URI baseURI = new File(MiscUtilities.constructPath(jEdit.getJEditHome(), "doc")).toURI();
  259. baseURL = baseURI.toURL().toString();
  260. }
  261. catch (MalformedURLException mu)
  262. {
  263. Log.log(Log.ERROR, this, mu);
  264. // what to do?
  265. }
  266. if (MiscUtilities.isURL(url))
  267. {
  268. if (url.startsWith(baseURL))
  269. {
  270. shortURL = url.substring(baseURL.length());
  271. if (shortURL.startsWith("/"))
  272. shortURL = shortURL.substring(1);
  273. }
  274. else
  275. {
  276. shortURL = url;
  277. }
  278. }
  279. else
  280. {
  281. shortURL = url;
  282. if (baseURL.endsWith("/"))
  283. url = baseURL + url;
  284. else
  285. url = baseURL + '/' + url;
  286. }
  287. if (url == null)
  288. return;
  289. url = url.trim();
  290. if (url.length() == 0)
  291. return;
  292. try
  293. {
  294. URL u = new URL(url);
  295. gotoURL(u, addToHistory, entry.getScrollBarPos());
  296. }
  297. catch (MalformedURLException mu)
  298. {
  299. urlField.setText(url);
  300. showError(props("infoviewer.error.badurl.message", new Object[] { mu }));
  301. }
  302. }
  303. /**
  304. * Convenience function
  305. *
  306. * @param url
  307. * @param addToHistory
  308. */
  309. public void gotoURL(URL url, boolean addToHistory)
  310. {
  311. gotoURL(url, addToHistory, 0);
  312. }
  313. /**
  314. * Displays the specified URL in the HTML component.
  315. *
  316. * @param url
  317. * The URL
  318. * @param addToHistory
  319. * Should the URL be added to the back/forward history?
  320. */
  321. public void gotoURL(URL url, boolean addToHistory, final int scrollBarPos)
  322. {
  323. if (url == null)
  324. return;
  325. String urlText = url.toString().trim();
  326. if (urlText.length() == 0)
  327. return;
  328. if (addToHistory)
  329. {
  330. history.add(getCurrentURL());
  331. HistoryModel hm = urlField.getModel();
  332. hm.addItem(urlText);
  333. }
  334. urlField.setText(urlText);
  335. viewer.setCursor(Cursor.getDefaultCursor());
  336. currentURL = new TitledURLEntry(urlText, urlText, scrollBarPos);
  337. currentStatus = LOADING;
  338. updateStatus();
  339. updateGoMenu();
  340. try
  341. {
  342. // viewer.getEditorKit().createDefaultDocument();
  343. viewer.setPage(url);
  344. // the style of the viewer
  345. if (viewer.getEditorKit() instanceof HTMLEditorKit)
  346. {
  347. HTMLEditorKit htmlEditorKit = (HTMLEditorKit) (viewer
  348. .getEditorKit());
  349. // HTMLDocument
  350. // doc=(HTMLDocument)viewer.getDocument();
  351. // Log.log(Log.DEBUG, this, "htmleditorkit in
  352. // use");
  353. StyleSheet styles;
  354. // StyleSheet
  355. // styles=htmlEditorKit.getStyleSheet();
  356. // if(doc!=null) {
  357. // Log.log(Log.DEBUG, this, "styles from doc");
  358. // styles=doc.getStyleSheet();
  359. // code below dies with NPE then
  360. // }
  361. // else {
  362. // Log.log(Log.DEBUG, this, "styles from
  363. // editor");
  364. styles = htmlEditorKit.getStyleSheet();
  365. // }
  366. Enumeration rules;
  367. if (styles != null)
  368. {
  369. // list available styles (which contain
  370. // 'font-size')
  371. rules = styles.getStyleNames();
  372. while (rules.hasMoreElements())
  373. {
  374. String name = (String) rules.nextElement();
  375. Style rule = styles.getStyle(name);
  376. if (rule.toString().indexOf("font-size") > -1)
  377. {
  378. Log.log(Log.DEBUG, this, name + "[old] : "
  379. + rule.toString());
  380. }
  381. }
  382. // make body fontsize smaller
  383. Style bodyrule = styles.getStyle("body");
  384. Style bodyruleparent = (Style) bodyrule.getResolveParent();
  385. if (bodyrule != null)
  386. {
  387. styles.removeStyle("body");
  388. Style newbodyrule = styles.addStyle("body",
  389. bodyruleparent);
  390. if (bodyruleparent != null)
  391. Log.log(Log.DEBUG, this, "bodyrule.p="
  392. + bodyruleparent.toString());
  393. Log.log(Log.DEBUG, this, "bodyrule.1="
  394. + bodyrule.toString());
  395. // String
  396. // val=(String)bodyrule.getAttribute("font-size");
  397. // Log.log(Log.DEBUG, this,
  398. // "body.font-size="+val);
  399. // bodyrule.removeAttribute("font-size");
  400. Enumeration attrs = bodyrule.getAttributeNames();
  401. if (attrs != null)
  402. {
  403. // Log.log(Log.DEBUG,
  404. // this, "copying
  405. // attributes");
  406. while (attrs.hasMoreElements())
  407. {
  408. Object name = attrs.nextElement();
  409. // Log.log(Log.DEBUG,
  410. // this, "
  411. // attribute.name="+name.toString());
  412. if (!name.toString().equals("font-size"))
  413. {
  414. newbodyrule.addAttribute(name,
  415. bodyrule.getAttribute(name));
  416. }
  417. }
  418. }
  419. // Action myaction=new
  420. // StyledEditorKit.FontSizeAction("new
  421. // font size",
  422. // Integer.parseInt(size));
  423. // myaction.actionPerformed(null);
  424. // HTMLDocument doc =
  425. // ((HTMLDocument)
  426. // viewer.getDocument());
  427. // doc.setCharacterAttributes(0,
  428. // doc.getLength(), newbodyrule,
  429. // true);
  430. /*
  431. * Log.log(Log.DEBUG, this,
  432. * "bodyrule.2="+bodyrule.toString());
  433. * bodyrule.addAttribute("font-size","10pt");
  434. * Log.log(Log.DEBUG, this,
  435. * "bodyrule.3="+bodyrule.toString());
  436. * newbodyrule.addAttributes(bodyrule);
  437. */
  438. Log.log(Log.DEBUG, this, "bodyrule.2="
  439. + newbodyrule.toString());
  440. }
  441. // styles.setBaseFontSize(1);
  442. // htmlEditorKit.setStyleSheet(styles);
  443. viewer.repaint();
  444. // list available styles (which contain
  445. // 'font-size')
  446. rules = styles.getStyleNames();
  447. while (rules.hasMoreElements())
  448. {
  449. String name = (String) rules.nextElement();
  450. Style rule = styles.getStyle(name);
  451. if (rule.toString().indexOf("font-size") > -1)
  452. {
  453. Log.log(Log.DEBUG, this, name + "[new] : "
  454. + rule.toString());
  455. }
  456. }
  457. }
  458. else
  459. {
  460. // Log.log(Log.WARNING, this, "empty
  461. // style set");
  462. }
  463. }
  464. else
  465. {
  466. // Log.log(Log.WARNING, this, "unexpected kind
  467. // of editorkit in use");
  468. }
  469. }
  470. catch (FileNotFoundException fnf)
  471. {
  472. String[] args = { urlText };
  473. showError(props("infoviewer.error.filenotfound.message", args));
  474. }
  475. catch (IOException io)
  476. {
  477. Log.log(Log.ERROR, this, io);
  478. String[] args = { urlText, io.getMessage() };
  479. showError(props("infoviewer.error.ioerror.message", args));
  480. }
  481. catch (Exception ex)
  482. {
  483. Log.log(Log.ERROR, this,
  484. "JEditorPane.setPage() threw an exception, probably a Swing bug:", ex);
  485. }
  486. finally
  487. {
  488. updateTimers();
  489. previousScrollBarValue = scrollBarPos;
  490. }
  491. }
  492. /**
  493. * Show the contents of the current jEdit buffer in InfoViewer.
  494. */
  495. public void gotoBufferURL()
  496. {
  497. Buffer buffer = view.getBuffer();
  498. String url = buffer.getPath();
  499. if (buffer.getVFS() instanceof FileVFS)
  500. url = "file:" + url;
  501. gotoURL(url, false, 0);
  502. }
  503. /**
  504. * Go forward in history. Beep if that's not possible.
  505. */
  506. public void forward()
  507. {
  508. TitledURLEntry ent = history.getNext(getCurrentURL());
  509. if (ent == null)
  510. getToolkit().beep();
  511. else
  512. gotoURL(ent, false);
  513. }
  514. /**
  515. * Go back in history. Beep, if that's not possible.
  516. */
  517. public void back()
  518. {
  519. TitledURLEntry prevURL = history.getPrevious(getCurrentURL());
  520. if (prevURL == null) {
  521. getToolkit().beep();
  522. }
  523. else {
  524. gotoURL(prevURL, false);
  525. }
  526. }
  527. /**
  528. * Reload the current URL.
  529. */
  530. public void reload()
  531. {
  532. if (currentURL == null)
  533. return;
  534. previousScrollBarValue = scrViewer.getVerticalScrollBar().getValue();
  535. // Clear the viewer and flush viewers' memorized URL:
  536. viewer.getDocument().putProperty(Document.StreamDescriptionProperty, null);
  537. gotoURL(getCurrentURL(), false);
  538. }
  539. /**
  540. * Add the current page to the bookmark list.
  541. */
  542. public void addToBookmarks()
  543. {
  544. if (currentURL == null)
  545. {
  546. GUIUtilities.error(null, "infoviewer.error.nourl", null);
  547. return;
  548. }
  549. jEdit.setProperty("infoviewer.bookmarks.title." + bookmarks.getSize(), currentURL
  550. .getTitle());
  551. jEdit.setProperty("infoviewer.bookmarks.url." + bookmarks.getSize(), currentURL
  552. .getURL());
  553. bookmarks.add(currentURL);
  554. jEdit.unsetProperty("infoviewer.bookmarks.title." + bookmarks.getSize());
  555. jEdit.unsetProperty("infoviewer.bookmarks.url." + bookmarks.getSize());
  556. // add menu item
  557. JMenuItem mi = new JMenuItem(currentURL.getTitle());
  558. mBmarks.add(mi);
  559. mi.setActionCommand(currentURL.getURL());
  560. mi.addActionListener(bookmarkhandler);
  561. }
  562. /**
  563. * Return the JEditorPane instance that is used to view HTML and text
  564. * URLs.
  565. */
  566. public JEditorPane getViewer()
  567. {
  568. return viewer;
  569. }
  570. /**
  571. * From interface HyperlinkListener: called when a hyperlink is clicked,
  572. * entered or leaved.
  573. */
  574. public void hyperlinkUpdate(HyperlinkEvent evt)
  575. {
  576. URL url = evt.getURL();
  577. if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
  578. {
  579. if (evt instanceof HTMLFrameHyperlinkEvent)
  580. {
  581. ((HTMLDocument) viewer.getDocument())
  582. .processHTMLFrameHyperlinkEvent((HTMLFrameHyperlinkEvent) evt);
  583. }
  584. else
  585. {
  586. if (url != null)
  587. gotoURL(url, true, 0);
  588. }
  589. }
  590. else if (evt.getEventType() == HyperlinkEvent.EventType.ENTERED)
  591. {
  592. viewer.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
  593. if (url != null)
  594. setStatusText(url.toString());
  595. }
  596. else if (evt.getEventType() == HyperlinkEvent.EventType.EXITED)
  597. {
  598. viewer.setCursor(Cursor.getDefaultCursor());
  599. updateStatus();
  600. }
  601. }
  602. /**
  603. * From interface PropertyChangeListener: called, when a property is
  604. * changed. This is used to listen for "page" property change events,
  605. * which occur, when the page is loaded completely.
  606. */
  607. public void propertyChange(PropertyChangeEvent e)
  608. {
  609. if ("page".equals(e.getPropertyName()))
  610. pageComplete();
  611. }
  612. /**
  613. * From interface EBComponent: Listen for messages on the EditBus.
  614. * Currently it listens for PropertiesChanged messages, to update any
  615. * bookmark changes.
  616. */
  617. public void handleMessage(EBMessage msg)
  618. {
  619. if (msg instanceof EditPaneUpdate)
  620. {
  621. EditPaneUpdate emsg = (EditPaneUpdate) msg;
  622. EditPane editPane = emsg.getEditPane();
  623. if (editPane == view.getEditPane())
  624. {
  625. if (emsg.getWhat() == EditPaneUpdate.BUFFER_CHANGED)
  626. {
  627. if (jEdit.getBooleanProperty("infoviewer.autoupdate")
  628. && jEdit
  629. .getBooleanProperty("infoviewer.autoupdate.onSwitch"))
  630. {
  631. gotoBufferURL();
  632. }
  633. }
  634. }
  635. }
  636. else if (msg instanceof BufferUpdate)
  637. {
  638. BufferUpdate bmsg = (BufferUpdate) msg;
  639. if (bmsg.getWhat() == BufferUpdate.DIRTY_CHANGED
  640. && bmsg.getBuffer() == view.getBuffer()
  641. && !bmsg.getBuffer().isDirty())
  642. {
  643. // buffer save detected
  644. if (jEdit.getBooleanProperty("infoviewer.autoupdate")
  645. && jEdit.getBooleanProperty("infoviewer.autoupdate.onSave") )
  646. {
  647. reload();
  648. }
  649. }
  650. }
  651. else if (msg instanceof PropertiesChanged)
  652. {
  653. updateBookmarksMenu();
  654. updateGoMenu();
  655. updateTimers();
  656. }
  657. }
  658. public void addNotify()
  659. {
  660. super.addNotify();
  661. EditBus.addToBus(this);
  662. }
  663. public void focusAddressBar()
  664. {
  665. urlField.requestFocus(true);
  666. }
  667. public void focusOnDefaultComponent()
  668. {
  669. viewer.requestFocus();
  670. }
  671. public void removeNotify()
  672. {
  673. super.removeNotify();
  674. EditBus.removeFromBus(this);
  675. if (periodicTimer != null)
  676. periodicTimer.stop();
  677. }
  678. public String getShortURL()
  679. {
  680. return shortURL;
  681. }
  682. public String getBaseURL()
  683. {
  684. return baseURL;
  685. }
  686. // {{{ createActions ()
  687. private void createActions()
  688. {
  689. aOpenFile = new infoviewer.actions.open_file();
  690. aOpenBuffer = new infoviewer.actions.open_buffer();
  691. aEditURL = new infoviewer.actions.edit_url();
  692. aReload = new infoviewer.actions.reload();
  693. aClose = new infoviewer.actions.close();
  694. aCopy = new infoviewer.actions.copy();
  695. aSelectAll = new infoviewer.actions.select_all();
  696. aBack = new infoviewer.actions.back();
  697. aOpenLocation = new infoviewer.actions.OpenLocation();
  698. aForward = new infoviewer.actions.forward();
  699. aHome = new infoviewer.actions.home();
  700. aBookmarksAdd = new infoviewer.actions.bookmarks_add();
  701. aBookmarksEdit = new infoviewer.actions.bookmarks_edit();
  702. aToggleSidebar = new infoviewer.actions.ToggleSidebar();
  703. aAbout = new infoviewer.actions.about();
  704. aFollowLink = new infoviewer.actions.follow_link();
  705. }
  706. // }}}
  707. // {{{ createMenu()
  708. private JMenuBar createMenu()
  709. {
  710. // File menu
  711. JMenu mFile = new JMenu(props("infoviewer.menu.file"));
  712. mFile.setMnemonic(props("infoviewer.menu.file.mnemonic").charAt(0));
  713. mFile.add(aOpenFile);
  714. mFile.add(aOpenBuffer);
  715. mFile.add(aEditURL);
  716. mFile.add(aReload);
  717. mFile.add(new JSeparator());
  718. mFile.add(aClose);
  719. // Edit menu
  720. JMenu mEdit = new JMenu(props("infoviewer.menu.edit"));
  721. mEdit.setMnemonic(props("infoviewer.menu.edit.mnemonic").charAt(0));
  722. mEdit.add(aCopy);
  723. mEdit.add(aSelectAll);
  724. // View menu
  725. JMenu mView = new JMenu(props("infoviewer.menu.view"));
  726. mView.setMnemonic(props("infoviewer.menu.view.mnemonic").charAt(0));
  727. JMenuItem item = aToggleSidebar.menuItem();
  728. mView.add(item);
  729. // Goto menu
  730. mGoto = new JMenu(props("infoviewer.menu.goto"));
  731. mGoto.setMnemonic(props("infoviewer.menu.goto.mnemonic").charAt(0));
  732. updateGoMenu();
  733. // Bookmarks menu
  734. mBmarks = new JMenu(props("infoviewer.menu.bmarks"));
  735. mBmarks.setMnemonic(props("infoviewer.menu.bmarks.mnemonic").charAt(0));
  736. updateBookmarksMenu();
  737. // Help menu
  738. mHelp = new JMenu(props("infoviewer.menu.help"));
  739. mHelp.setMnemonic(props("infoviewer.menu.help.mnemonic").charAt(0));
  740. updateHelpMenu();
  741. // Menubar
  742. JMenuBar mb = new JMenuBar();
  743. mb.add(mFile);
  744. mb.add(mEdit);
  745. mb.add(mView);
  746. mb.add(mGoto);
  747. mb.add(mBmarks);
  748. mb.add(mHelp);
  749. return mb;
  750. }
  751. // }}}
  752. // {{{
  753. private JToolBar createToolbar()
  754. {
  755. EnhancedJToolBar tb = new EnhancedJToolBar(JToolBar.HORIZONTAL);
  756. tb.add(aBack);
  757. tb.add(aForward);
  758. tb.add(aReload);
  759. tb.add(aHome);
  760. tb.add(aOpenFile);
  761. tb.add(aEditURL);
  762. tb.add(aOpenBuffer);
  763. tb.add(Box.createHorizontalGlue());
  764. bStartStop = new JButton(ICON_ANIM)
  765. {
  766. private static final long serialVersionUID = 3350768542711107896L;
  767. // Otherwise the animated gif keeps calling this method
  768. // even when
  769. // the component is no longer visible, causing a memory
  770. // leak.
  771. public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
  772. int h)
  773. {
  774. if (!isDisplayable())
  775. return false;
  776. else
  777. return super.imageUpdate(img, infoflags, x, y, w, h);
  778. }
  779. };
  780. bStartStop.setDisabledIcon(ICON_NOANIM);
  781. bStartStop.setBorderPainted(false);
  782. bStartStop.setEnabled(false);
  783. tb.add(bStartStop);
  784. return tb;
  785. }
  786. // }}}
  787. public void toggleSideBar()
  788. {
  789. }
  790. private JPanel createAddressBar()
  791. {
  792. // the url textfield
  793. urlField = new HistoryTextField("infoviewer");
  794. urlField.setFocusAccelerator('l');
  795. urlField.addActionListener(new ActionListener()
  796. {
  797. public void actionPerformed(ActionEvent evt)
  798. {
  799. gotoURL(urlField.getText(), true, -1);
  800. }
  801. });
  802. // url textfield and label
  803. JPanel panel = new JPanel(new BorderLayout());
  804. panel.add(new JLabel(props("infoviewer.label.gotoURL")), BorderLayout.WEST);
  805. panel.add(urlField, BorderLayout.CENTER);
  806. return panel;
  807. }
  808. private JPanel createStatusBar()
  809. {
  810. // the status text field
  811. status = new JLabel(GREET);
  812. status.setBorder(new BevelBorder(BevelBorder.LOWERED));
  813. status.setFont(new Font("Dialog", Font.PLAIN, 10));
  814. status.setMinimumSize(new Dimension(100, status.getPreferredSize().height));
  815. // the title text field
  816. title = new JLabel("No Document");
  817. title.setBorder(new BevelBorder(BevelBorder.LOWERED));
  818. title.setFont(new Font("Dialog", Font.PLAIN, 10));
  819. title.setMinimumSize(new Dimension(100, title.getPreferredSize().height));
  820. // status and title field
  821. JPanel statusBar = new JPanel(new GridLayout(1, 0));
  822. statusBar.add(status);
  823. statusBar.add(title);
  824. return statusBar;
  825. }
  826. /**
  827. * Update the bookmarks menu according to the bookmarks stored in the
  828. * properties.
  829. */
  830. private synchronized void updateBookmarksMenu()
  831. {
  832. mBmarks.removeAll();
  833. mBmarks.add(aBookmarksAdd);
  834. mBmarks.add(aBookmarksEdit);
  835. mBmarks.add(new JSeparator());
  836. // add bookmarks
  837. bookmarks = new Bookmarks();
  838. for (int i = 0; i < bookmarks.getSize(); i++)
  839. {
  840. String title = bookmarks.getTitle(i);
  841. if (title.length() > 0 && title.charAt(0) == '-')
  842. mBmarks.add(new JSeparator());
  843. else
  844. {
  845. JMenuItem mi = new JMenuItem(title);
  846. mBmarks.add(mi);
  847. mi.setActionCommand(bookmarks.getURL(i));
  848. mi.addActionListener(bookmarkhandler);
  849. }
  850. }
  851. }
  852. private void updateHelpMenu()
  853. {
  854. mHelp.removeAll();
  855. mHelp.add(aAbout);
  856. // add a menu item for the docs
  857. JMenuItem mi = new JMenuItem(props("infoviewer.menu.help.readme"));
  858. mi.setActionCommand(props("infoviewer.menu.help.readme.url"));
  859. mi.addActionListener(bookmarkhandler);
  860. mi.setMnemonic(props("infoviewer.menu.help.readme.mnemonic").charAt(0));
  861. mHelp.add(mi);
  862. }
  863. private synchronized void updateGoMenu()
  864. {
  865. mGoto.removeAll();
  866. mGoto.add(aOpenLocation);
  867. mGoto.add(aBack);
  868. mGoto.add(aForward);
  869. mGoto.add(aHome);
  870. mGoto.add(new JSeparator());
  871. // add history
  872. TitledURLEntry[] entr = history.getGoMenuEntries();
  873. int pos = history.getHistoryPos();
  874. for (int i = 0; i < entr.length; i++)
  875. {
  876. JMenuItem mi = new JMenuItem(entr[i].getTitle(),
  877. entr[i].equals(currentURL) ? ICON_CHECK : ICON_NOCHECK);
  878. mi.setActionCommand(entr[i].getURL());
  879. mi.addActionListener(historyhandler);
  880. mGoto.add(mi);
  881. }
  882. }
  883. private synchronized void updateGoMenuTitles()
  884. {
  885. TitledURLEntry[] entr = history.getGoMenuEntries();
  886. for (int i = 0; i < entr.length; i++)
  887. {
  888. JMenuItem mi = mGoto.getItem(i + 5);
  889. if (mi == null)
  890. {
  891. mi = new JMenuItem();
  892. mGoto.add(mi);
  893. }
  894. mi.setText(entr[i].getTitle());
  895. }
  896. }
  897. private void updateActions()
  898. {
  899. SwingUtilities.invokeLater(new Runnable()
  900. {
  901. public void run()
  902. {
  903. aForward.setEnabled(history.hasNext());
  904. aBack.setEnabled(history.hasPrevious());
  905. aEditURL.setEnabled(currentURL != null);
  906. bStartStop.setEnabled(currentStatus == LOADING);
  907. }
  908. });
  909. }
  910. private void updateStatus()
  911. {
  912. switch (currentStatus)
  913. {
  914. case LOADING:
  915. setStatusText(props("infoviewer.status.loading", new Object[] { currentURL
  916. .getURL() }));
  917. break;
  918. case READY:
  919. int size = viewer.getDocument().getLength();
  920. setStatusText(props("infoviewer.status.ready", new Integer[] { new Integer(
  921. size) }));
  922. break;
  923. case ERROR:
  924. setStatusText(props("infoviewer.status.error"));
  925. break;
  926. default:
  927. setStatusText(GREET);
  928. break;
  929. }
  930. updateActions();
  931. }
  932. private void updateTimers()
  933. {
  934. if (periodicTimer != null)
  935. periodicTimer.stop();
  936. if (jEdit.getBooleanProperty("infoviewer.autoupdate"))
  937. {
  938. if (jEdit.getBooleanProperty("infoviewer.autoupdate.periodically"))
  939. {
  940. try
  941. {
  942. periodicDelay = Integer
  943. .parseInt(jEdit
  944. .getProperty("infoviewer.autoupdate.periodically.delay"));
  945. }
  946. catch (NumberFormatException e)
  947. {
  948. periodicDelay = 20000;
  949. }
  950. periodicTimer = new Timer(periodicDelay, new ActionListener()
  951. {
  952. public void actionPerformed(ActionEvent evt)
  953. {
  954. if (currentStatus != LOADING && currentURL != null)
  955. {
  956. Log.log(Log.DEBUG, this,
  957. "periodic update (every "
  958. + periodicDelay + "ms): "
  959. + currentURL);
  960. reload();
  961. }
  962. }
  963. });
  964. periodicTimer.setInitialDelay(periodicDelay);
  965. periodicTimer.setRepeats(true);
  966. periodicTimer.setCoalesce(true);
  967. periodicTimer.start();
  968. }
  969. }
  970. }
  971. private void pageComplete()
  972. {
  973. // restore previous vertical scrollbar value, if page was
  974. // reloaded
  975. if (previousScrollBarValue >= 0)
  976. {
  977. JScrollBar jsb = scrViewer.getVerticalScrollBar();
  978. if (previousScrollBarValue < jsb.getMaximum())
  979. jsb.setValue(previousScrollBarValue);
  980. else jsb.setValue(jsb.getMaximum());
  981. }
  982. // try to get the title of the document
  983. Document doc = viewer.getDocument();
  984. if (doc != null)
  985. {
  986. String newTitle = getTitleFromDocument(doc);
  987. if (currentURL != null)
  988. {
  989. currentURL.setTitle(newTitle);
  990. }
  991. // set the new window title
  992. setTitle(newTitle);
  993. // update title in the "Go" menu history
  994. updateGoMenuTitles();
  995. }
  996. currentStatus = READY;
  997. updateStatus();
  998. }
  999. /** try to get the title of the document */
  1000. private String getTitleFromDocument(Document doc)
  1001. {
  1002. Object obj = doc.getProperty(Document.TitleProperty);
  1003. if (obj == null)
  1004. return currentURL != null ? currentURL.getURL()
  1005. : props("infoviewer.notitle");
  1006. else
  1007. return obj.toString();
  1008. }
  1009. private void setStatusText(final String text)
  1010. {
  1011. SwingUtilities.invokeLater(new Runnable()
  1012. {
  1013. public void run()
  1014. {
  1015. status.setText(text);
  1016. }
  1017. });
  1018. }
  1019. private void setTitle(final String text)
  1020. {
  1021. SwingUtilities.invokeLater(new Runnable()
  1022. {
  1023. public void run()
  1024. {
  1025. title.setText(text);
  1026. }
  1027. });
  1028. }
  1029. protected void dismiss()
  1030. {
  1031. DockableWindowManager dwm = jEdit.getActiveView().getDockableWindowManager();
  1032. String name = getName();
  1033. dwm.hideDockableWindow(name);
  1034. }
  1035. protected void showError(String errortext)
  1036. {
  1037. viewer.getDocument().putProperty(Document.StreamDescriptionProperty, null);
  1038. viewer.getEditorKit().createDefaultDocument();
  1039. viewer.setContentType("text/html");
  1040. viewer.setText("<html><head></head><body>\n" + "<h1>Error</h1><p>\n" + errortext
  1041. + "\n</body></html>");
  1042. currentURL = null;
  1043. currentStatus = ERROR;
  1044. updateStatus();
  1045. }
  1046. /** convenience method for jEdit.getProperty(String). */
  1047. private static String props(String key)
  1048. {
  1049. return jEdit.getProperty(key);
  1050. }
  1051. /** convenience method for jEdit.getProperty(String,Object[]). */
  1052. private static String props(String key, Object[] args)
  1053. {
  1054. return jEdit.getProperty(key, args);
  1055. }
  1056. // greet string
  1057. private final static String GREET = props("infoviewer.greetstring", new Object[] {
  1058. props("infoviewer.title"), props("plugin.infoviewer.InfoViewerPlugin.version") });
  1059. // status numbers for updateStatus()
  1060. private final static int LOADING = 1;
  1061. private final static int READY = 2;
  1062. private final static int ERROR = 3;
  1063. // icons
  1064. private final static ImageIcon ICON_ANIM = new ImageIcon(InfoViewer.class
  1065. .getResource("images/fish_anim.gif"));
  1066. private final static ImageIcon ICON_NOANIM = new ImageIcon(InfoViewer.class
  1067. .getResource("images/fish.gif"));
  1068. private final static ImageIcon ICON_CHECK = new ImageIcon(InfoViewer.class
  1069. .getResource("images/checkmenu_check.gif"));
  1070. private final static ImageIcon ICON_NOCHECK = new ImageIcon(InfoViewer.class
  1071. .getResource("images/checkmenu_nocheck.gif"));
  1072. // infoviewer actions
  1073. private InfoViewerAction aOpenFile;
  1074. private InfoViewerAction aOpenBuffer;
  1075. private InfoViewerAction aEditURL;
  1076. private InfoViewerAction aReload;
  1077. private InfoViewerAction aClose;
  1078. private InfoViewerAction aCopy;
  1079. private InfoViewerAction aSelectAll;
  1080. private InfoViewerAction aBack;
  1081. private InfoViewerAction aOpenLocation;
  1082. private InfoViewerAction aForward;
  1083. private InfoViewerAction aHome;
  1084. private InfoViewerAction aBookmarksAdd;
  1085. private InfoViewerAction aBookmarksEdit;
  1086. private InfoViewerAction aAbout;
  1087. protected ToggleSidebar aToggleSidebar;
  1088. private infoviewer.actions.follow_link aFollowLink;
  1089. // gui elements
  1090. private JLabel status;
  1091. private JLabel title;
  1092. private EnhancedJEditorPane viewer;
  1093. protected HistoryTextField urlField;
  1094. private JButton bStartStop;
  1095. private JMenu mGoto;
  1096. private JMenu mBmarks;
  1097. private JMenu mHelp;
  1098. // misc
  1099. private View view;
  1100. private TitledURLEntry currentURL;
  1101. private int currentStatus;
  1102. private Bookmarks bookmarks;
  1103. private History history;
  1104. private URLButtonHandler bookmarkhandler;
  1105. private URLButtonHandler historyhandler;
  1106. private boolean isDocked;
  1107. private Timer periodicTimer;
  1108. private int periodicDelay;
  1109. private int previousScrollBarValue;
  1110. protected String baseURL;
  1111. private String shortURL;
  1112. private ActionContext actionContext;
  1113. private ActionSet actionSet;
  1114. private class URLButtonHandler implements ActionListener
  1115. {
  1116. private boolean addToHistory = true;
  1117. public URLButtonHandler(boolean addToHistory)
  1118. {
  1119. this.addToHistory = addToHistory;
  1120. }
  1121. /**
  1122. * A bookmark was selected in the Bookmarks menu. Open the
  1123. * corresponding URL in the InfoViewer. The URL will be added to
  1124. * the history, if this URLButtonHandler was initialized with
  1125. * <code>addToHistory = true</code>.
  1126. */
  1127. public void actionPerformed(ActionEvent evt)
  1128. {
  1129. String cmd = evt.getActionCommand();
  1130. gotoURL(cmd, addToHistory, -1);
  1131. }
  1132. }
  1133. private class MouseHandler extends MouseAdapter
  1134. {
  1135. JPopupMenu popup = null;
  1136. public void mousePressed(MouseEvent evt)
  1137. {
  1138. if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
  1139. {
  1140. evt.consume();
  1141. AccessibleText txt = viewer.getAccessibleContext()
  1142. .getAccessibleText();
  1143. if (txt != null && txt instanceof AccessibleHypertext)
  1144. {
  1145. AccessibleHypertext hyp = (AccessibleHypertext) txt;
  1146. int charIndex = hyp.getIndexAtPoint(evt.getPoint());
  1147. int linkIndex = hyp.getLinkIndex(charIndex);
  1148. if (linkIndex >= 0)
  1149. {
  1150. // user clicked on a link
  1151. aFollowLink.setEnabled(true);
  1152. aFollowLink.setClickPoint(evt.getPoint());
  1153. }
  1154. else
  1155. aFollowLink.setEnabled(false);
  1156. }
  1157. JPopupMenu popup = getPopup();
  1158. popup.show(viewer, evt.getX() - 1, evt.getY() - 1);
  1159. }
  1160. }
  1161. private JPopupMenu getPopup()
  1162. {
  1163. if (popup == null)
  1164. {
  1165. popup = new JPopupMenu();
  1166. popup.add(aBack);
  1167. popup.add(aForward);
  1168. popup.addSeparator();
  1169. popup.add(aEditURL);
  1170. popup.add(aOpenBuffer);
  1171. popup.add(aReload);
  1172. popup.addSeparator();
  1173. popup.add(aFollowLink);
  1174. }
  1175. return popup;
  1176. }
  1177. }
  1178. public class ArrowKeyHandler
  1179. {
  1180. int[] keys;
  1181. public ArrowKeyHandler()
  1182. {
  1183. keys = new int[] { KeyEvent.VK_UP, KeyEvent.VK_DOWN, KeyEvent.VK_LEFT,
  1184. KeyEvent.VK_RIGHT, KeyEvent.VK_HOME, KeyEvent.VK_END,
  1185. KeyEvent.VK_PAGE_UP, KeyEvent.VK_PAGE_DOWN };
  1186. Arrays.sort(keys);
  1187. }
  1188. public void processKeyEvent(KeyEvent e)
  1189. {
  1190. if (Arrays.binarySearch(keys, e.getID()) > -1)
  1191. {
  1192. scrViewer.processKeyEvent(e);
  1193. e.consume();
  1194. }
  1195. }
  1196. }
  1197. class KeyHandler extends KeyAdapter
  1198. {
  1199. public void keyPressed(KeyEvent evt)
  1200. {
  1201. if (evt.getKeyCode() == KeyEvent.VK_ESCAPE)
  1202. {
  1203. dismiss();
  1204. evt.consume();
  1205. }
  1206. }
  1207. }
  1208. class MyScrollPane extends JScrollPane
  1209. {
  1210. public MyScrollPane(JComponent c)
  1211. {
  1212. super(c);
  1213. };
  1214. public void processKeyEvent(KeyEvent e)
  1215. {
  1216. super.processKeyEvent(e);
  1217. }
  1218. private static final long serialVersionUID = 390984816401470412L;
  1219. }
  1220. public static final long serialVersionUID = 1236527;
  1221. }