/plugins/InfoViewer/tags/release-1-5-8/infoviewer/InfoViewer.java

# · Java · 1361 lines · 956 code · 186 blank · 219 comment · 131 complexity · 9516f802a31168cb589faee6a84398ce MD5 · raw file

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