001: package net.sourceforge.squirrel_sql.client.gui;
002:
003: /*
004: * Copyright (C) 2003-2004 Colin Bell
005: * colbell@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.awt.BorderLayout;
022: import java.awt.Container;
023: import java.awt.Dimension;
024: import java.awt.Font;
025: import java.io.File;
026: import java.io.FileWriter;
027: import java.io.IOException;
028: import java.io.PrintWriter;
029: import java.net.MalformedURLException;
030: import java.net.URL;
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.List;
034: import java.util.Map;
035:
036: import javax.swing.BorderFactory;
037: import javax.swing.ImageIcon;
038: import javax.swing.JFrame;
039: import javax.swing.JScrollPane;
040: import javax.swing.JSplitPane;
041: import javax.swing.JTree;
042: import javax.swing.SwingUtilities;
043: import javax.swing.event.TreeSelectionEvent;
044: import javax.swing.event.TreeSelectionListener;
045: import javax.swing.tree.DefaultMutableTreeNode;
046: import javax.swing.tree.DefaultTreeCellRenderer;
047: import javax.swing.tree.DefaultTreeModel;
048: import javax.swing.tree.MutableTreeNode;
049: import javax.swing.tree.TreePath;
050:
051: import net.sourceforge.squirrel_sql.fw.gui.StatusBar;
052: import net.sourceforge.squirrel_sql.fw.util.BaseException;
053: import net.sourceforge.squirrel_sql.fw.util.StringManager;
054: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
055: import net.sourceforge.squirrel_sql.fw.util.StringUtilities;
056: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
057: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
058:
059: import net.sourceforge.squirrel_sql.client.IApplication;
060: import net.sourceforge.squirrel_sql.client.plugin.PluginInfo;
061: import net.sourceforge.squirrel_sql.client.resources.SquirrelResources;
062: import net.sourceforge.squirrel_sql.client.util.ApplicationFiles;
063:
064: /**
065: * This window shows the SQuirreL Help files.
066: *
067: * @author <A HREF="mailto:colbell@users.sourceforge.net">Colin Bell</A>
068: */
069: public class HelpViewerWindow extends JFrame {
070: private static final long serialVersionUID = 1L;
071:
072: /** Logger for this class. */
073: private final static ILogger s_log = LoggerController
074: .createLogger(HelpViewerWindow.class);
075:
076: /** Internationalized strings for this class. */
077: private static final StringManager s_stringMgr = StringManagerFactory
078: .getStringManager(HelpViewerWindow.class);
079:
080: /** Application API. */
081: private final IApplication _app;
082:
083: /** Tree containing a node for each help document. */
084: private JTree _tree;
085:
086: /** Panel that displays the help document. */
087: private HtmlViewerPanel _detailPnl;
088:
089: /** Statusbar at bottom of window. */
090: private StatusBar _statusBar = new StatusBar();
091:
092: /** Home URL. */
093: private URL _homeURL;
094:
095: /** Collection of the nodes in the tree keyed by the URL.toString(). */
096: private final Map<String, DefaultMutableTreeNode> _nodes = new HashMap<String, DefaultMutableTreeNode>();
097:
098: /**
099: * Ctor.
100: *
101: * @param app Application API.
102: *
103: * @throws IllegalArgumentException
104: * Thrown if <TT>null</TT> <TT>IApplication</TT> passed.
105: */
106: public HelpViewerWindow(IApplication app)
107: throws IllegalArgumentException, BaseException {
108: // i18n[HelpViewerWindow.title=SQuirreL SQL Client Help]
109: super (s_stringMgr.getString("HelpViewerWindow.title"));
110: if (app == null) {
111: throw new IllegalArgumentException("IApplication == null");
112: }
113: _app = app;
114: try {
115: createGUI();
116: } catch (IOException ex) {
117: throw new BaseException(ex);
118: }
119: }
120:
121: /**
122: * Set the Document displayed to that defined by the passed URL.
123: *
124: * @param url URL of document to be displayed.
125: */
126: private void setSelectedDocument(URL url) {
127: try {
128: _detailPnl.gotoURL(url);
129: // i18n[HelpViewerWindow.pageloaded=Page loaded.]
130: _statusBar.setText(s_stringMgr
131: .getString("HelpViewerWindow.pageloaded"));
132: } catch (IOException ex) {
133: // i18n[HelpViewerWindow.error.displaydocument=Error displaying document]
134: s_log
135: .error(
136: s_stringMgr
137: .getString("HelpViewerWindow.error.displaydocument"),
138: ex);
139: _statusBar.setText(ex.toString());
140: }
141: }
142:
143: private void selectTreeNodeForURL(URL url) {
144: // Strip local part of URL.
145: String key = url.toString();
146: final int idx = key.lastIndexOf('#');
147: if (idx > -1) {
148: key = key.substring(0, idx);
149: }
150: DefaultMutableTreeNode node = _nodes.get(key);
151: if (node != null) // && node != _tree.getLastSelectedPathComponent())
152: {
153: DefaultTreeModel model = (DefaultTreeModel) _tree
154: .getModel();
155: TreePath path = new TreePath(model.getPathToRoot(node));
156: if (path != null) {
157: _tree.expandPath(path);
158: _tree.scrollPathToVisible(path);
159: _tree.setSelectionPath(path);
160: }
161: }
162: }
163:
164: /**
165: * Create user interface.
166: */
167: private void createGUI() throws IOException {
168: setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
169: final SquirrelResources rsrc = _app.getResources();
170: final ImageIcon icon = rsrc
171: .getIcon(SquirrelResources.IImageNames.VIEW);
172: if (icon != null) {
173: setIconImage(icon.getImage());
174: }
175:
176: Container contentPane = getContentPane();
177: contentPane.setLayout(new BorderLayout());
178:
179: JSplitPane splitPane = new JSplitPane(
180: JSplitPane.HORIZONTAL_SPLIT);
181: splitPane.setBorder(BorderFactory.createEmptyBorder());
182: splitPane.setOneTouchExpandable(true);
183: splitPane.setContinuousLayout(true);
184: splitPane.add(createContentsTree(), JSplitPane.LEFT);
185: splitPane.add(createDetailsPanel(), JSplitPane.RIGHT);
186: contentPane.add(splitPane, BorderLayout.CENTER);
187: splitPane.setDividerLocation(200);
188:
189: contentPane.add(new HtmlViewerPanelToolBar(_app, _detailPnl),
190: BorderLayout.NORTH);
191:
192: Font fn = _app.getFontInfoStore().getStatusBarFontInfo()
193: .createFont();
194: _statusBar.setFont(fn);
195: contentPane.add(_statusBar, BorderLayout.SOUTH);
196:
197: pack();
198:
199: SwingUtilities.invokeLater(new Runnable() {
200: public void run() {
201: _detailPnl.setHomeURL(_homeURL);
202: _tree.expandRow(0);
203: _tree.expandRow(2);
204: if (_app.getSquirrelPreferences().isFirstRun()) {
205: _tree.setSelectionRow(1);
206: } else {
207: _tree.setSelectionRow(3);
208: }
209: _tree.setRootVisible(false);
210: }
211: });
212:
213: _detailPnl.addListener(new IHtmlViewerPanelListener() {
214: public void currentURLHasChanged(
215: HtmlViewerPanelListenerEvent evt) {
216: selectTreeNodeForURL(evt.getHtmlViewerPanel().getURL());
217: }
218:
219: public void homeURLHasChanged(
220: HtmlViewerPanelListenerEvent evt) {
221: // Nothing to do.
222: }
223: });
224: }
225:
226: /**
227: * Create a tree each node being a link to a document.
228: *
229: * @return The contents tree.
230: */
231: private JScrollPane createContentsTree() throws IOException {
232: final ApplicationFiles appFiles = new ApplicationFiles();
233: // i18n[HelpViewerWindow.help=Help]
234: final FolderNode root = new FolderNode(s_stringMgr
235: .getString("HelpViewerWindow.help"));
236: _tree = new JTree(new DefaultTreeModel(root));
237: _tree.setShowsRootHandles(true);
238: _tree
239: .addTreeSelectionListener(new ObjectTreeSelectionListener());
240:
241: // Renderer for tree.
242: DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
243: SquirrelResources rsrc = _app.getResources();
244: renderer.setLeafIcon(rsrc
245: .getIcon(SquirrelResources.IImageNames.HELP_TOPIC));
246: renderer.setOpenIcon(rsrc
247: .getIcon(SquirrelResources.IImageNames.HELP_TOC_OPEN));
248: renderer
249: .setClosedIcon(rsrc
250: .getIcon(SquirrelResources.IImageNames.HELP_TOC_CLOSED));
251: _tree.setCellRenderer(renderer);
252:
253: // First put the Welcome to SQuirreL node.
254: File file = appFiles.getWelcomeFile();
255: try {
256: // i18n[HelpViewerWindow.welcome=Welcome]
257: DocumentNode dn = new DocumentNode(s_stringMgr
258: .getString("HelpViewerWindow.welcome"), file);
259: root.add(dn);
260: _nodes.put(dn.getURL().toString(), dn);
261: } catch (MalformedURLException ex) {
262: // i18n[HelpViewerWindow.error.loadwelcomefile=Error retrieving Welcome file URL for {0}]
263: String msg = s_stringMgr.getString(
264: "HelpViewerWindow.error.loadwelcomefile", file
265: .getAbsolutePath());
266: s_log.error(msg, ex);
267: }
268:
269: // Add Help, Licence and Change Log nodes to the tree.
270: // i18n[HelpViewerWindow.help=Help]
271: final FolderNode helpRoot = new FolderNode(s_stringMgr
272: .getString("HelpViewerWindow.help"));
273: root.add(helpRoot);
274: _nodes.put(helpRoot.getURL().toString(), helpRoot);
275: // i18n[HelpViewerWindow.licences=Licences]
276: final FolderNode licenceRoot = new FolderNode(s_stringMgr
277: .getString("HelpViewerWindow.licences"));
278: root.add(licenceRoot);
279: _nodes.put(licenceRoot.getURL().toString(), licenceRoot);
280: // i18n[HelpViewerWindow.changelogs=Change Logs]
281: final FolderNode changeLogRoot = new FolderNode(s_stringMgr
282: .getString("HelpViewerWindow.changelogs"));
283: root.add(changeLogRoot);
284: _nodes.put(changeLogRoot.getURL().toString(), changeLogRoot);
285:
286: // Add SQuirreL help to the Help node.
287: file = appFiles.getQuickStartGuideFile();
288: try {
289: // i18n[HelpViewerWindow.squirrel=SQuirreL]
290: DocumentNode dn = new DocumentNode(s_stringMgr
291: .getString("HelpViewerWindow.squirrel"), file);
292: helpRoot.add(dn);
293: _homeURL = dn.getURL();
294: _nodes.put(_homeURL.toString(), dn);
295: } catch (MalformedURLException ex) {
296: // i18n[HelpViewerWindow.error.loadwelcomefile=Error retrieving Help file URL for {0}]
297: String msg = s_stringMgr.getString(
298: "HelpViewerWindow.error.loadhelpfile", file
299: .getAbsolutePath());
300: s_log.error(msg, ex);
301: }
302:
303: // Add SQuirreL Licence to the Licence node.
304: file = appFiles.getLicenceFile();
305: try {
306: // i18n[HelpViewerWindow.squirrel=SQuirreL]
307: DocumentNode dn = new DocumentNode(s_stringMgr
308: .getString("HelpViewerWindow.squirrel"), file);
309: licenceRoot.add(dn);
310: _nodes.put(dn.getURL().toString(), dn);
311: } catch (MalformedURLException ex) {
312: // i18n[HelpViewerWindow.error.loadlicencefile=Error retrieving Licence file URL for {0}]
313: String msg = s_stringMgr.getString(
314: "HelpViewerWindow.error.loadlicencefile", file
315: .getAbsolutePath());
316: s_log.error(msg, ex);
317: }
318:
319: // Add SQuirreL Change Log to the Licence node.
320: file = appFiles.getChangeLogFile();
321: try {
322: // i18n[HelpViewerWindow.squirrel=SQuirreL]
323: DocumentNode dn = new DocumentNode(s_stringMgr
324: .getString("HelpViewerWindow.squirrel"), file);
325: changeLogRoot.add(dn);
326: _nodes.put(dn.getURL().toString(), dn);
327: } catch (MalformedURLException ex) {
328: // i18n[HelpViewerWindow.error.loadchangelogfile=Error retrieving Change Log file URL for {0}]
329: String msg = s_stringMgr.getString(
330: "HelpViewerWindow.error.loadchangelogfile", file
331: .getAbsolutePath());
332: s_log.error(msg, ex);
333: }
334:
335: // Add plugin help, licence and change log documents to the tree.
336: PluginInfo[] pi = _app.getPluginManager()
337: .getPluginInformation();
338: for (int i = 0; i < pi.length; ++i) {
339: try {
340: final File dir = pi[i].getPlugin()
341: .getPluginAppSettingsFolder();
342: final String title = pi[i].getDescriptiveName();
343:
344: // Help document.
345: try {
346: final String fn = pi[i].getHelpFileName();
347: if (fn != null && fn.length() > 0) {
348: DocumentNode dn = new DocumentNode(title,
349: new File(dir, fn));
350: helpRoot.add(dn);
351: _nodes.put(dn.getURL().toString(), dn);
352: }
353: } catch (IOException ex) {
354: // i18n[HelpViewerWindow.error.loadpluginhelp=Error generating Help entry for plugin {0}]
355: String msg = s_stringMgr.getString(
356: "HelpViewerWindow.error.loadpluginhelp",
357: pi[i].getDescriptiveName());
358: s_log.error(msg, ex);
359: }
360:
361: // Licence document.
362: try {
363: final String fn = pi[i].getLicenceFileName();
364: if (fn != null && fn.length() > 0) {
365: DocumentNode dn = new DocumentNode(title,
366: new File(dir, fn));
367: licenceRoot.add(dn);
368: _nodes.put(dn.getURL().toString(), dn);
369: }
370: } catch (IOException ex) {
371: // i18n[HelpViewerWindow.error.loadpluginlicence=Error generating Licence entry for plugin {0}]
372: String msg = s_stringMgr.getString(
373: "HelpViewerWindow.error.loadpluginlicence",
374: pi[i].getDescriptiveName());
375: s_log.error(msg, ex);
376: }
377:
378: try {
379: // Change log.
380: final String fn = pi[i].getChangeLogFileName();
381: if (fn != null && fn.length() > 0) {
382: DocumentNode dn = new DocumentNode(title,
383: new File(dir, fn));
384: changeLogRoot.add(dn);
385: _nodes.put(dn.getURL().toString(), dn);
386: }
387: } catch (IOException ex) {
388: // i18n[HelpViewerWindow.error.loadchangelog=Error generating Change Log entry for plugin {0}]
389: String msg = s_stringMgr.getString(
390: "HelpViewerWindow.error.loadchangelog",
391: pi[i].getDescriptiveName());
392: s_log.error(msg, ex);
393: }
394: } catch (IOException ex) {
395: // i18n[HelpViewerWindow.error.loadpluginsettings=Error retrieving app settings folder for plugin {0}]
396: String msg = s_stringMgr.getString(
397: "HelpViewerWindow.error.loadpluginsettings",
398: pi[i].getDescriptiveName());
399: s_log.error(msg, ex);
400: }
401: }
402:
403: // FAQ.
404: file = appFiles.getFAQFile();
405: try {
406: // i18n[HelpViewerWindow.faq=FAQ]
407: DocumentNode dn = new DocumentNode(s_stringMgr
408: .getString("HelpViewerWindow.faq"), file);
409: root.add(dn);
410: _nodes.put(dn.getURL().toString(), dn);
411: } catch (MalformedURLException ex) {
412: // i18n[HelpViewerWindow.error.loadfaqfile=Error retrieving FAQ from URL = {0}]
413: String msg = s_stringMgr.getString(
414: "HelpViewerWindow.error.loadfaqfile", file
415: .getAbsolutePath());
416: s_log.error(msg, ex);
417: }
418:
419: // generate contents file.
420: helpRoot.generateContentsFile();
421: licenceRoot.generateContentsFile();
422: changeLogRoot.generateContentsFile();
423:
424: JScrollPane sp = new JScrollPane(_tree);
425: sp.setPreferredSize(new Dimension(200, 200));
426:
427: return sp;
428: }
429:
430: HtmlViewerPanel createDetailsPanel() {
431: _detailPnl = new HtmlViewerPanel(null);
432: return _detailPnl;
433: }
434:
435: private class DocumentNode extends DefaultMutableTreeNode {
436: private static final long serialVersionUID = 1L;
437:
438: private URL _url;
439:
440: DocumentNode(String title, File file)
441: throws MalformedURLException {
442: super (title, false);
443: setFile(file);
444: }
445:
446: DocumentNode(String title, boolean allowsChildren) {
447: super (title, allowsChildren);
448: }
449:
450: URL getURL() {
451: return _url;
452: }
453:
454: void setFile(File file) throws MalformedURLException {
455: _url = file.toURI().toURL();
456: }
457: }
458:
459: private class FolderNode extends DocumentNode {
460: private static final long serialVersionUID = 1L;
461: private final List<String> _docTitles = new ArrayList<String>();
462: private final List<URL> _docURLs = new ArrayList<URL>();
463: private final File _contentsFile;
464:
465: FolderNode(String title) throws IOException {
466: super (title, true);
467: _contentsFile = File.createTempFile("sqschelp", "html");
468: _contentsFile.deleteOnExit();
469: setFile(_contentsFile);
470: }
471:
472: public void add(MutableTreeNode node) {
473: super .add(node);
474: if (node instanceof DocumentNode) {
475: final DocumentNode dn = (DocumentNode) node;
476: final URL docURL = dn.getURL();
477: if (docURL != null) {
478: String docTitle = dn.toString();
479: if (StringUtilities.isEmpty(docTitle)) {
480: docTitle = docURL.toExternalForm();
481: }
482: _docTitles.add(docTitle);
483: _docURLs.add(docURL);
484: }
485: }
486: }
487:
488: synchronized void generateContentsFile() {
489: try {
490: final PrintWriter pw = new PrintWriter(new FileWriter(
491: _contentsFile));
492: try {
493: StringBuffer buf = new StringBuffer(50);
494: buf.append("<HTML><BODY><H1>").append(toString())
495: .append("</H1>");
496: pw.println(buf.toString());
497: for (int i = 0, limit = _docTitles.size(); i < limit; ++i) {
498: // final String docTitle = (String)_docTitles.get(i);
499: final URL docUrl = _docURLs.get(i);
500: buf = new StringBuffer(50);
501: buf.append("<A HREF=\"").append(docUrl).append(
502: "\">").append(_docTitles.get(i))
503: .append("</A><BR>");
504: pw.println(buf.toString());
505: }
506: pw.println("</BODY></HTML");
507: } finally {
508: pw.close();
509: }
510: } catch (IOException ex) {
511: // i18n[HelpViewerWindow.error.congen=Error generating Contents file]
512: String msg = s_stringMgr
513: .getString("HelpViewerWindow.error.congen");
514: s_log.error(msg, ex);
515: _statusBar.setText(msg);
516: }
517: }
518: }
519:
520: /**
521: * This class listens for changes in the node selected in the tree
522: * and displays the appropriate help document for the node.
523: */
524: private final class ObjectTreeSelectionListener implements
525: TreeSelectionListener {
526: public void valueChanged(TreeSelectionEvent evt) {
527: final TreePath path = evt.getNewLeadSelectionPath();
528: if (path != null) {
529: Object lastComp = path.getLastPathComponent();
530: if (lastComp instanceof DocumentNode) {
531: setSelectedDocument(((DocumentNode) lastComp)
532: .getURL());
533: }
534: }
535: }
536: }
537: }
|