001: /*
002: * Help.java
003: *
004: * Copyright (C) 1998-2003 Peter Graves
005: * $Id: Help.java,v 1.14 2003/07/18 17:15:05 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program 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
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import java.io.BufferedReader;
025: import java.io.BufferedWriter;
026: import java.io.IOException;
027: import java.io.InputStreamReader;
028: import java.io.OutputStreamWriter;
029: import java.io.Writer;
030: import java.util.Comparator;
031: import java.util.Collections;
032: import java.util.List;
033: import org.armedbear.lisp.Closure;
034: import org.armedbear.lisp.LispObject;
035:
036: public final class Help {
037: public static final void help() {
038: help(null);
039: }
040:
041: public static void help(String arg) {
042: final Editor editor = Editor.currentEditor();
043: final Frame frame = editor.getFrame();
044: final File dir = getDocumentationDirectory();
045: if (dir == null)
046: return;
047: frame.setWaitCursor();
048: try {
049: String fileName = "contents.html";
050: String ref = null;
051: if (arg == null || arg.length() == 0)
052: ;
053: else if (arg.endsWith(".html")) {
054: File file = File.getInstance(dir, arg);
055: if (file != null && file.isFile())
056: fileName = arg;
057: } else {
058: Command command = CommandTable.getCommand(arg);
059: if (command != null) {
060: fileName = "commands.html";
061: ref = command.getName();
062: } else {
063: Property property = Property.findProperty(arg);
064: if (property != null) {
065: fileName = "preferences.html";
066: ref = property.getDisplayName();
067: }
068: }
069: }
070: File file = File.getInstance(dir, fileName);
071: if (file == null || !file.isFile())
072: return;
073: Buffer buf = null;
074: // Look for existing help buffer.
075: if (isHelpBuffer(editor.getBuffer()))
076: buf = editor.getBuffer();
077: else {
078: for (BufferIterator it = new BufferIterator(); it
079: .hasNext();) {
080: Buffer b = it.nextBuffer();
081: if (isHelpBuffer(b)) {
082: buf = b;
083: break;
084: }
085: }
086: }
087: if (buf != null) {
088: // Found existing help buffer.
089: // Save history.
090: int offset = 0;
091: if (editor.getBuffer() == buf) {
092: offset = buf.getAbsoluteOffset(editor.getDot());
093: } else {
094: View view = buf.getLastView();
095: if (view != null) {
096: Position dot = view.getDot();
097: if (dot != null)
098: offset = buf.getAbsoluteOffset(dot);
099: }
100: }
101: ((WebBuffer) buf).saveHistory(buf.getFile(), offset,
102: ((WebBuffer) buf).getContentType());
103: if (!buf.getFile().equals(file)) {
104: // Existing buffer is not looking at the right file.
105: ((WebBuffer) buf).go(file, 0, null);
106: }
107: Position pos = ((WebBuffer) buf).findRef(ref);
108: if (editor.getBuffer() == buf) {
109: if (pos != null)
110: editor.moveDotTo(pos);
111: } else {
112: editor.makeNext(buf);
113: Editor ed = editor.activateInOtherWindow(buf);
114: if (pos != null) {
115: ed.moveDotTo(pos);
116: ed.updateDisplay();
117: }
118: }
119: } else {
120: buf = WebBuffer.createWebBuffer(file, null, ref);
121: Editor otherEditor = editor.getOtherEditor();
122: if (otherEditor != null) {
123: buf.setUnsplitOnClose(otherEditor.getBuffer()
124: .unsplitOnClose());
125: otherEditor.makeNext(buf);
126: } else {
127: buf.setUnsplitOnClose(true);
128: editor.makeNext(buf);
129: }
130: Editor ed = editor.activateInOtherWindow(buf);
131: ed.updateDisplay();
132: }
133: } finally {
134: frame.setDefaultCursor();
135: }
136: }
137:
138: private static boolean isHelpBuffer(Buffer buffer) {
139: if (!(buffer instanceof WebBuffer))
140: return false;
141: File file = buffer.getFile();
142: if (file != null) {
143: File dir = file.getParentFile();
144: if (dir != null && dir.equals(getDocumentationDirectory()))
145: return true;
146: }
147: return false;
148: }
149:
150: public static final File getBindingsFile() {
151: return File.getInstance(Directories.getTempDirectory(),
152: "bindings.html");
153: }
154:
155: public static void listBindings() {
156: final Editor editor = Editor.currentEditor();
157: final Frame frame = editor.getFrame();
158: frame.setWaitCursor();
159: try {
160: File file = getBindingsFile();
161: BufferedWriter writer = new BufferedWriter(
162: new OutputStreamWriter(file.getOutputStream()));
163: writer
164: .write("<html>\n<head>\n<title>Keyboard Bindings</title>\n</head>\n<body>\n");
165: File docDir = getDocumentationDirectory();
166: writer.write("<b>");
167: writer.write("Local Bindings (");
168: writer.write(editor.getMode().toString());
169: writer.write(" mode)");
170: writer.write("</b><br><br>");
171: addBindingsFromKeyMap(
172: editor.getBuffer().getKeyMapForMode(), docDir,
173: writer);
174: writer.write("<br>");
175: writer.write("<b>");
176: writer.write("Global Bindings");
177: writer.write("</b><br><br>");
178: addBindingsFromKeyMap(KeyMap.getGlobalKeyMap(), docDir,
179: writer);
180: writer.write("</body>\n</html>\n");
181: writer.flush();
182: writer.close();
183: if (isListBindingsBuffer(editor.getBuffer())) {
184: ((WebBuffer) editor.getBuffer()).go(file, 0,
185: "text/html");
186: } else {
187: Buffer buf = null;
188: for (BufferIterator it = new BufferIterator(); it
189: .hasNext();) {
190: Buffer b = it.nextBuffer();
191: if (isListBindingsBuffer(b)) {
192: buf = b;
193: break;
194: }
195: }
196: if (buf != null)
197: ((WebBuffer) buf).go(file, 0, "text/html");
198: else
199: buf = WebBuffer.createWebBuffer(file, null, null);
200: Editor otherEditor = editor.getOtherEditor();
201: if (otherEditor != null) {
202: buf.setUnsplitOnClose(otherEditor.getBuffer()
203: .unsplitOnClose());
204: otherEditor.makeNext(buf);
205: } else {
206: buf.setUnsplitOnClose(true);
207: editor.makeNext(buf);
208: }
209: editor.activateInOtherWindow(buf);
210: }
211: } catch (IOException e) {
212: Log.error(e);
213: } finally {
214: frame.setDefaultCursor();
215: }
216: }
217:
218: private static void addBindingsFromKeyMap(KeyMap keyMap,
219: File docDir, Writer writer) throws IOException {
220: KeyMapping[] mappings = keyMap.getMappings();
221: int count = mappings.length;
222: if (count == 0) {
223: writer.write("[None]<br>\n");
224: return;
225: }
226: for (int i = 0; i < count; i++) {
227: KeyMapping mapping = mappings[i];
228: FastStringBuffer sb = new FastStringBuffer(64);
229: sb.append(mapping.getKeyText());
230: Object command = mapping.getCommand();
231: if (command instanceof String) {
232: String commandString = (String) command;
233: int spaces = 32 - sb.length();
234: for (int j = spaces; j-- > 0;)
235: sb.append(" ");
236: if (docDir != null) {
237: sb.append("<a href=\"");
238: sb.append(docDir.canonicalPath());
239: sb.append(LocalFile.getSeparatorChar());
240: sb.append("commands.html#");
241: sb.append(commandString);
242: sb.append("\">");
243: sb.append(commandString);
244: sb.append("</a>");
245: } else
246: sb.append(commandString);
247: sb.append("<br>\n");
248: } else if (command instanceof LispObject) {
249: int spaces = 32 - sb.length();
250: for (int j = spaces; j-- > 0;)
251: sb.append(" ");
252: String name = ((LispObject) command).getName();
253: if (name != null) {
254: sb.append(name);
255: } else if (command instanceof Closure) {
256: sb.append("#<CLOSURE ");
257: sb.append("@ ");
258: sb.append(Integer.toHexString(command.hashCode()));
259: sb.append(">");
260: }
261: sb.append("<br>\n");
262: }
263: writer.write(sb.toString());
264: }
265: }
266:
267: private static boolean isListBindingsBuffer(Buffer buffer) {
268: if (!(buffer instanceof WebBuffer))
269: return false;
270: return buffer.getFile().equals(getBindingsFile());
271: }
272:
273: public static void apropos() {
274: final Editor editor = Editor.currentEditor();
275: InputDialog d = new InputDialog(editor, "Apropos:", "Apropos",
276: null);
277: d.setHistory(new History("apropos"));
278: editor.centerDialog(d);
279: d.show();
280: String arg = d.getInput();
281: if (arg == null)
282: return;
283: arg = arg.trim();
284: if (arg.length() == 0)
285: return;
286: apropos(arg);
287: }
288:
289: public static void apropos(String arg) {
290: final File dir = getDocumentationDirectory();
291: if (dir == null)
292: return;
293: final Editor editor = Editor.currentEditor();
294: final Frame frame = editor.getFrame();
295: frame.setWaitCursor();
296: try {
297: File file = getAproposFile();
298: BufferedWriter writer = new BufferedWriter(
299: new OutputStreamWriter(file.getOutputStream()));
300: writer.write("<html>\n<head>\n<title>");
301: writer.write("Apropos ");
302: writer.write('"');
303: writer.write(arg);
304: writer.write('"');
305: writer.write("</title>\n</head>\n<body>\n");
306: writer.write("<b>");
307: writer.write("Commands");
308: writer.write("</b><br><br>");
309: File helpFile = File.getInstance(dir, "commands.html");
310: if (helpFile != null && !helpFile.isFile())
311: helpFile = null;
312: List commands = CommandTable.apropos(arg);
313: sort(commands);
314: addAproposEntries(commands, helpFile, writer);
315: writer.write("<br>");
316: writer.write("<b>");
317: writer.write("Preferences");
318: writer.write("</b><br><br>");
319: helpFile = File.getInstance(dir, "preferences.html");
320: if (helpFile != null && !helpFile.isFile())
321: helpFile = null;
322: List properties = Property.apropos(arg);
323: sort(properties);
324: addAproposEntries(properties, helpFile, writer);
325: writer.write("</body>\n</html>\n");
326: writer.flush();
327: writer.close();
328: Editor ed;
329: if (isAproposBuffer(editor.getBuffer())) {
330: ((WebBuffer) editor.getBuffer()).go(file, 0,
331: "text/html");
332: ed = editor;
333: } else {
334: Buffer buf = null;
335: for (BufferIterator it = new BufferIterator(); it
336: .hasNext();) {
337: Buffer b = it.nextBuffer();
338: if (isAproposBuffer(b)) {
339: buf = b;
340: break;
341: }
342: }
343: if (buf != null)
344: ((WebBuffer) buf).go(file, 0, "text/html");
345: else {
346: buf = WebBuffer.createWebBuffer(file, null, null);
347: buf.setTransient(true);
348: }
349: Editor otherEditor = editor.getOtherEditor();
350: if (otherEditor != null) {
351: buf.setUnsplitOnClose(otherEditor.getBuffer()
352: .unsplitOnClose());
353: otherEditor.makeNext(buf);
354: } else {
355: buf.setUnsplitOnClose(true);
356: editor.makeNext(buf);
357: }
358: ed = editor.activateInOtherWindow(buf);
359: }
360: for (Line line = ed.getBuffer().getFirstLine(); line != null; line = line
361: .next()) {
362: if (WebBuffer.findLink(line, 0) != null) {
363: int offset = 0;
364: while (offset < line.length()) {
365: char c = line.charAt(offset);
366: if (!Character.isWhitespace(c) && c != 160)
367: break;
368: ++offset;
369: }
370: ed.moveDotTo(line, offset);
371: ed.updateDisplay();
372: break;
373: }
374: }
375: } catch (IOException e) {
376: Log.error(e);
377: } finally {
378: frame.setDefaultCursor();
379: }
380: }
381:
382: private static Comparator comparator;
383:
384: private static void sort(List list) {
385: if (comparator == null) {
386: comparator = new Comparator() {
387: public int compare(Object o1, Object o2) {
388: return o1.toString().compareToIgnoreCase(
389: o2.toString());
390: }
391: };
392: }
393: Collections.sort(list, comparator);
394: }
395:
396: private static void addAproposEntries(List list, File helpFile,
397: Writer writer) throws IOException {
398: int size = 0;
399: if (list != null)
400: size = list.size();
401: if (size > 0) {
402: for (int i = 0; i < size; i++) {
403: String s = (String) list.get(i);
404: if (helpFile != null) {
405: writer.write(" <a href=\"");
406: writer.write(helpFile.canonicalPath());
407: writer.write('#');
408: writer.write(s);
409: writer.write("\">");
410: }
411: writer.write(s);
412: if (helpFile != null)
413: writer.write("</a>");
414: writer.write("<br>\n");
415: }
416: } else
417: writer.write(" <i>None</i><br>\n");
418: }
419:
420: private static boolean isAproposBuffer(Buffer buffer) {
421: if (!(buffer instanceof WebBuffer))
422: return false;
423: return buffer.getFile().equals(getAproposFile());
424: }
425:
426: private static final File getAproposFile() {
427: return File.getInstance(Directories.getTempDirectory(),
428: "apropos.html");
429: }
430:
431: public static File getDocumentationDirectory() {
432: String s = Editor.preferences().getStringProperty(
433: Property.DOC_PATH);
434: if (s != null) {
435: Path path = new Path(s);
436: String[] array = path.list();
437: if (array != null) {
438: for (int i = 0; i < array.length; i++) {
439: File dir = File.getInstance(array[i]);
440: if (isDocDir(dir))
441: return dir;
442: }
443: }
444: }
445: s = System.getProperty("java.class.path");
446: if (s != null) {
447: final File userDir = File.getInstance(System
448: .getProperty("user.dir"));
449: Path path = new Path(s);
450: String[] array = path.list();
451: if (array != null) {
452: for (int i = 0; i < array.length; i++) {
453: String filename = array[i];
454: if (filename.toLowerCase().endsWith("j.jar")) {
455: File jarFile = File.getInstance(userDir,
456: filename);
457: if (jarFile != null && jarFile.isFile()) {
458: File jarDir = jarFile.getParentFile();
459: if (jarDir != null && jarDir.isDirectory()) {
460: File docDir = File.getInstance(jarDir,
461: "doc");
462: if (isDocDir(docDir))
463: return docDir;
464: }
465: }
466: } else if (filename.toLowerCase().endsWith("src")) {
467: // "~/j/src"
468: File srcDir = File.getInstance(userDir,
469: filename);
470: if (srcDir != null && srcDir.isDirectory()) {
471: File parentDir = srcDir.getParentFile(); // "~/j"
472: if (parentDir != null
473: && parentDir.isDirectory()) {
474: File docDir = File.getInstance(
475: parentDir, "doc"); // "~/j/doc"
476: if (isDocDir(docDir))
477: return docDir;
478: }
479: }
480: } else {
481: String suffix = LocalFile.getSeparator() + "j"
482: + LocalFile.getSeparator() + "j.jar";
483: if (filename.endsWith(suffix)) {
484: // "/usr/local/share/j/j.jar"
485: File dataDir = File.getInstance(filename
486: .substring(0, filename.length()
487: - suffix.length())); // "/usr/local/share"
488: File docDir = File.getInstance(dataDir,
489: "doc" + LocalFile.getSeparator()
490: + "j"); // "/usr/local/share/doc/j"
491: if (isDocDir(docDir))
492: return docDir;
493: }
494: }
495: }
496: }
497: }
498: // As a last resort, a couple of hard-coded possibilities...
499: if (Platform.isPlatformUnix()) {
500: File dir = File.getInstance("/usr/local/share/doc/j");
501: if (isDocDir(dir))
502: return dir;
503: dir = File.getInstance("/usr/share/doc/j");
504: if (isDocDir(dir))
505: return dir;
506: } else if (Platform.isPlatformWindows()) {
507: String dirname = cygpath("/usr/local/share/doc/j");
508: if (dirname != null) {
509: File dir = File.getInstance(dirname);
510: if (isDocDir(dir))
511: return dir;
512: }
513: }
514: return null;
515: }
516:
517: private static String cygpath(String s) {
518: String[] cmdarray = { "cygpath", "-w", s };
519: try {
520: Process process = Runtime.getRuntime().exec(cmdarray);
521: BufferedReader reader = new BufferedReader(
522: new InputStreamReader(process.getInputStream()));
523: return reader.readLine();
524: } catch (Throwable t) {
525: return null;
526: }
527: }
528:
529: // Return true if dir seems to contain j's documentation.
530: private static boolean isDocDir(File dir) {
531: if (dir == null || !dir.isDirectory())
532: return false;
533: File check = File.getInstance(dir, "commands.html");
534: if (check == null || !check.isFile())
535: return false;
536: return true;
537: }
538: }
|