001: /*
002: * HelpViewerDialog.java - HTML Help viewer
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 1999, 2005 Slava Pestov, Nicholas O'Leary
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.help;
024:
025: //{{{ Imports
026: import java.awt.BorderLayout;
027: import java.awt.Component;
028: import java.awt.Cursor;
029: import java.awt.Dimension;
030: import java.awt.Font;
031:
032: import java.awt.event.ActionEvent;
033: import java.awt.event.ActionListener;
034: import java.awt.event.KeyAdapter;
035: import java.awt.event.KeyEvent;
036:
037: import java.beans.PropertyChangeEvent;
038: import java.beans.PropertyChangeListener;
039:
040: import java.io.File;
041: import java.io.IOException;
042:
043: import java.net.MalformedURLException;
044: import java.net.URL;
045:
046: import javax.swing.Box;
047: import javax.swing.BoxLayout;
048: import javax.swing.JEditorPane;
049: import javax.swing.JFrame;
050: import javax.swing.JLabel;
051: import javax.swing.JPanel;
052: import javax.swing.JScrollBar;
053: import javax.swing.JScrollPane;
054: import javax.swing.JSplitPane;
055: import javax.swing.JTabbedPane;
056: import javax.swing.SwingUtilities;
057:
058: import javax.swing.event.HyperlinkEvent;
059: import javax.swing.event.HyperlinkEvent.EventType;
060: import javax.swing.event.HyperlinkListener;
061:
062: import javax.swing.text.html.HTMLDocument;
063: import javax.swing.text.html.HTMLFrameHyperlinkEvent;
064:
065: import org.gjt.sp.jedit.EBComponent;
066: import org.gjt.sp.jedit.EBMessage;
067: import org.gjt.sp.jedit.EditBus;
068: import org.gjt.sp.jedit.GUIUtilities;
069: import org.gjt.sp.jedit.jEdit;
070: import org.gjt.sp.jedit.MiscUtilities;
071:
072: import org.gjt.sp.jedit.msg.PluginUpdate;
073: import org.gjt.sp.jedit.msg.PropertiesChanged;
074:
075: import org.gjt.sp.util.Log;
076:
077: import static org.gjt.sp.jedit.help.HelpHistoryModel.HistoryEntry;
078:
079: //}}}
080:
081: /**
082: * jEdit's searchable help viewer. It uses a Swing JEditorPane to display the HTML,
083: * and implements a URL history.
084: * @author Slava Pestov
085: * @version $Id: HelpViewer.java 9197 2007-03-22 14:35:05Z Vampire0 $
086: */
087: public class HelpViewer extends JFrame implements HelpViewerInterface,
088: EBComponent, HelpHistoryModelListener {
089: //{{{ HelpViewer constructor
090: /**
091: * Creates a new help viewer with the default help page.
092: * @since jEdit 4.0pre4
093: */
094: public HelpViewer() {
095: this ("welcome.html");
096: } //}}}
097:
098: //{{{ HelpViewer constructor
099: /**
100: * Creates a new help viewer for the specified URL.
101: * @param url The URL
102: */
103: public HelpViewer(URL url) {
104: this (url.toString());
105: } //}}}
106:
107: //{{{ HelpViewer constructor
108: /**
109: * Creates a new help viewer for the specified URL.
110: * @param url The URL
111: */
112: public HelpViewer(String url) {
113: super (jEdit.getProperty("helpviewer.title"));
114:
115: setIconImage(GUIUtilities.getEditorIcon());
116:
117: try {
118: baseURL = new File(MiscUtilities.constructPath(jEdit
119: .getJEditHome(), "doc")).toURL().toString();
120: } catch (MalformedURLException mu) {
121: Log.log(Log.ERROR, this , mu);
122: // what to do?
123: }
124:
125: ActionHandler actionListener = new ActionHandler();
126:
127: JTabbedPane tabs = new JTabbedPane();
128: tabs.addTab(jEdit.getProperty("helpviewer.toc.label"),
129: toc = new HelpTOCPanel(this ));
130: tabs.addTab(jEdit.getProperty("helpviewer.search.label"),
131: new HelpSearchPanel(this ));
132: tabs.setMinimumSize(new Dimension(0, 0));
133:
134: JPanel rightPanel = new JPanel(new BorderLayout());
135:
136: Box toolBar = new Box(BoxLayout.X_AXIS);
137: //toolBar.setFloatable(false);
138:
139: toolBar.add(title = new JLabel());
140: toolBar.add(Box.createGlue());
141: historyModel = new HelpHistoryModel(25);
142: back = new HistoryButton(HistoryButton.BACK, historyModel);
143: back.addActionListener(actionListener);
144: toolBar.add(back);
145: forward = new HistoryButton(HistoryButton.FORWARD, historyModel);
146: forward.addActionListener(actionListener);
147: toolBar.add(forward);
148: back.setPreferredSize(forward.getPreferredSize());
149: rightPanel.add(BorderLayout.NORTH, toolBar);
150:
151: viewer = new JEditorPane();
152: viewer.setEditable(false);
153: viewer.addHyperlinkListener(new LinkHandler());
154: viewer.setFont(new Font("Monospaced", Font.PLAIN, 12));
155: viewer.addPropertyChangeListener(new PropertyChangeHandler());
156: viewer.addKeyListener(new KeyHandler());
157:
158: viewerScrollPane = new JScrollPane(viewer);
159:
160: rightPanel.add(BorderLayout.CENTER, viewerScrollPane);
161:
162: splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jEdit
163: .getBooleanProperty("appearance.continuousLayout"),
164: tabs, rightPanel);
165: splitter.setBorder(null);
166:
167: getContentPane().add(BorderLayout.CENTER, splitter);
168:
169: historyModel.addHelpHistoryModelListener(this );
170: historyUpdated();
171:
172: gotoURL(url, true, 0);
173:
174: setDefaultCloseOperation(DISPOSE_ON_CLOSE);
175:
176: getRootPane().setPreferredSize(new Dimension(750, 500));
177:
178: pack();
179: GUIUtilities.loadGeometry(this , "helpviewer");
180: GUIUtilities.addSizeSaver(this , "helpviewer");
181:
182: EditBus.addToBus(this );
183:
184: setVisible(true);
185:
186: SwingUtilities.invokeLater(new Runnable() {
187: public void run() {
188: splitter.setDividerLocation(jEdit.getIntegerProperty(
189: "helpviewer.splitter", 250));
190: viewer.requestFocus();
191: }
192: });
193: } //}}}
194:
195: //{{{ gotoURL() method
196: /**
197: * Displays the specified URL in the HTML component.
198: *
199: * @param url The URL
200: * @param addToHistory Should the URL be added to the back/forward
201: * history?
202: * @param scrollPosition The vertical scrollPosition
203: */
204: public void gotoURL(String url, boolean addToHistory,
205: final int scrollPosition) {
206: // the TOC pane looks up user's guide URLs relative to the
207: // doc directory...
208: String shortURL;
209: if (MiscUtilities.isURL(url)) {
210: if (url.startsWith(baseURL)) {
211: shortURL = url.substring(baseURL.length());
212: if (shortURL.startsWith("/")) {
213: shortURL = shortURL.substring(1);
214: }
215: } else {
216: shortURL = url;
217: }
218: } else {
219: shortURL = url;
220: if (baseURL.endsWith("/")) {
221: url = baseURL + url;
222: } else {
223: url = baseURL + '/' + url;
224: }
225: }
226:
227: // reset default cursor so that the hand cursor doesn't
228: // stick around
229: viewer.setCursor(Cursor.getDefaultCursor());
230:
231: try {
232: URL _url = new URL(url);
233:
234: if (!_url.equals(viewer.getPage())) {
235: title.setText(jEdit.getProperty("helpviewer.loading"));
236: } else {
237: /* don't show loading msg because we won't
238: receive a propertyChanged */
239: }
240:
241: historyModel.setCurrentScrollPosition(viewer.getPage(),
242: getCurrentScrollPosition());
243: viewer.setPage(_url);
244: if (0 != scrollPosition) {
245: SwingUtilities.invokeLater(new Runnable() {
246: public void run() {
247: viewerScrollPane.getVerticalScrollBar()
248: .setValue(scrollPosition);
249: }
250: });
251: }
252: if (addToHistory) {
253: historyModel.addToHistory(url);
254: }
255: } catch (MalformedURLException mf) {
256: Log.log(Log.ERROR, this , mf);
257: String[] args = { url, mf.getMessage() };
258: GUIUtilities.error(this , "badurl", args);
259: return;
260: } catch (IOException io) {
261: Log.log(Log.ERROR, this , io);
262: String[] args = { url, io.toString() };
263: GUIUtilities.error(this , "read-error", args);
264: return;
265: }
266:
267: this .shortURL = shortURL;
268:
269: // select the appropriate tree node.
270: if (shortURL != null) {
271: toc.selectNode(shortURL);
272: }
273:
274: viewer.requestFocus();
275: } //}}}
276:
277: //{{{ getCurrentScrollPosition() method
278: int getCurrentScrollPosition() {
279: return viewerScrollPane.getVerticalScrollBar().getValue();
280: } //}}}
281:
282: //{{{ getCurrentPage() method
283: URL getCurrentPage() {
284: return viewer.getPage();
285: } //}}}
286:
287: //{{{ dispose() method
288: public void dispose() {
289: EditBus.removeFromBus(this );
290: jEdit.setIntegerProperty("helpviewer.splitter", splitter
291: .getDividerLocation());
292: super .dispose();
293: } //}}}
294:
295: //{{{ handleMessage() method
296: public void handleMessage(EBMessage msg) {
297: if (msg instanceof PluginUpdate) {
298: PluginUpdate pmsg = (PluginUpdate) msg;
299: if (pmsg.getWhat() == PluginUpdate.LOADED
300: || pmsg.getWhat() == PluginUpdate.UNLOADED) {
301: if (!pmsg.isExiting()) {
302: if (!queuedTOCReload)
303: queueTOCReload();
304: queuedTOCReload = true;
305: }
306: }
307: } else if (msg instanceof PropertiesChanged) {
308: GUIUtilities.initContinuousLayout(splitter);
309: }
310: } //}}}
311:
312: //{{{ getBaseURL() method
313: public String getBaseURL() {
314: return baseURL;
315: } //}}}
316:
317: //{{{ getShortURL() method
318: public String getShortURL() {
319: return shortURL;
320: } //}}}
321:
322: //{{{ historyUpdated() method
323: public void historyUpdated() {
324: back.setEnabled(historyModel.hasPrevious());
325: forward.setEnabled(historyModel.hasNext());
326: } //}}}
327:
328: //{{{ getComponent method
329: public Component getComponent() {
330: return getRootPane();
331: } //}}}
332:
333: //{{{ Private members
334:
335: //{{{ Instance members
336: private String baseURL;
337: private String shortURL;
338: private HistoryButton back;
339: private HistoryButton forward;
340: private JEditorPane viewer;
341: private JScrollPane viewerScrollPane;
342: private JLabel title;
343: private JSplitPane splitter;
344: private HelpHistoryModel historyModel;
345: private HelpTOCPanel toc;
346: private boolean queuedTOCReload;
347:
348: //}}}
349:
350: //{{{ queueTOCReload() method
351: public void queueTOCReload() {
352: SwingUtilities.invokeLater(new Runnable() {
353: public void run() {
354: queuedTOCReload = false;
355: toc.load();
356: }
357: });
358: } //}}}
359:
360: //}}}
361:
362: //{{{ Inner classes
363:
364: //{{{ ActionHandler class
365: class ActionHandler implements ActionListener {
366: //{{{ actionPerformed() class
367: public void actionPerformed(ActionEvent evt) {
368: Object source = evt.getSource();
369: String actionCommand = evt.getActionCommand();
370: int separatorPosition = actionCommand.lastIndexOf(':');
371: String url;
372: int scrollPosition;
373: if (-1 == separatorPosition) {
374: url = actionCommand;
375: scrollPosition = 0;
376: } else {
377: url = actionCommand.substring(0, separatorPosition);
378: scrollPosition = Integer.parseInt(actionCommand
379: .substring(separatorPosition + 1));
380: }
381: if (url.length() != 0) {
382: gotoURL(url, false, scrollPosition);
383: return;
384: }
385:
386: if (source == back) {
387: HistoryEntry entry = historyModel.back(HelpViewer.this );
388: if (entry == null) {
389: getToolkit().beep();
390: } else {
391: gotoURL(entry.url, false, entry.scrollPosition);
392: }
393: } else if (source == forward) {
394: HistoryEntry entry = historyModel
395: .forward(HelpViewer.this );
396: if (entry == null) {
397: getToolkit().beep();
398: } else {
399: gotoURL(entry.url, false, entry.scrollPosition);
400: }
401: }
402: } //}}}
403: } //}}}
404:
405: //{{{ LinkHandler class
406: class LinkHandler implements HyperlinkListener {
407: //{{{ hyperlinkUpdate() method
408: public void hyperlinkUpdate(HyperlinkEvent evt) {
409: if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
410: if (evt instanceof HTMLFrameHyperlinkEvent) {
411: ((HTMLDocument) viewer.getDocument())
412: .processHTMLFrameHyperlinkEvent((HTMLFrameHyperlinkEvent) evt);
413: historyUpdated();
414: } else {
415: URL url = evt.getURL();
416: if (url != null) {
417: gotoURL(url.toString(), true, 0);
418: }
419: }
420: } else if (evt.getEventType() == HyperlinkEvent.EventType.ENTERED) {
421: viewer.setCursor(Cursor
422: .getPredefinedCursor(Cursor.HAND_CURSOR));
423: } else if (evt.getEventType() == HyperlinkEvent.EventType.EXITED) {
424: viewer.setCursor(Cursor.getDefaultCursor());
425: }
426: } //}}}
427: } //}}}
428:
429: //{{{ PropertyChangeHandler class
430: class PropertyChangeHandler implements PropertyChangeListener {
431: public void propertyChange(PropertyChangeEvent evt) {
432: if ("page".equals(evt.getPropertyName())) {
433: String titleStr = (String) viewer.getDocument()
434: .getProperty("title");
435: if (titleStr == null) {
436: titleStr = MiscUtilities.getFileName(viewer
437: .getPage().toString());
438: }
439: title.setText(titleStr);
440: historyModel.updateTitle(viewer.getPage().toString(),
441: titleStr);
442: }
443: }
444: } //}}}
445:
446: //{{{ KeyHandler class
447: private class KeyHandler extends KeyAdapter {
448: public void keyPressed(KeyEvent ke) {
449: switch (ke.getKeyCode()) {
450: case KeyEvent.VK_UP:
451: JScrollBar scrollBar = viewerScrollPane
452: .getVerticalScrollBar();
453: scrollBar.setValue(scrollBar.getValue()
454: - scrollBar.getUnitIncrement(-1));
455: ke.consume();
456: break;
457: case KeyEvent.VK_DOWN:
458: scrollBar = viewerScrollPane.getVerticalScrollBar();
459: scrollBar.setValue(scrollBar.getValue()
460: + scrollBar.getUnitIncrement(1));
461: ke.consume();
462: break;
463: case KeyEvent.VK_LEFT:
464: scrollBar = viewerScrollPane.getHorizontalScrollBar();
465: scrollBar.setValue(scrollBar.getValue()
466: - scrollBar.getUnitIncrement(-1));
467: ke.consume();
468: break;
469: case KeyEvent.VK_RIGHT:
470: scrollBar = viewerScrollPane.getHorizontalScrollBar();
471: scrollBar.setValue(scrollBar.getValue()
472: + scrollBar.getUnitIncrement(1));
473: ke.consume();
474: break;
475: }
476: }
477: } //}}}
478:
479: //}}}
480: }
|