001: /*
002: * DockableWindowFactory.java - loads dockables.xml, etc
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2005 Slava Pestov
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.gui;
024:
025: //{{{ Imports
026: import java.io.IOException;
027: import java.net.URL;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.LinkedList;
031: import java.util.Map;
032: import java.util.Stack;
033:
034: import javax.swing.JComponent;
035:
036: import org.gjt.sp.jedit.ActionSet;
037: import org.gjt.sp.jedit.BeanShell;
038: import org.gjt.sp.jedit.EditAction;
039: import org.gjt.sp.jedit.MiscUtilities;
040: import org.gjt.sp.jedit.PluginJAR;
041: import org.gjt.sp.jedit.View;
042: import org.gjt.sp.jedit.jEdit;
043: import org.gjt.sp.util.Log;
044: import org.gjt.sp.util.XMLUtilities;
045: import org.xml.sax.Attributes;
046: import org.xml.sax.InputSource;
047: import org.xml.sax.helpers.DefaultHandler;
048:
049: import org.gjt.sp.jedit.bsh.NameSpace;
050: import org.gjt.sp.jedit.bsh.UtilEvalError;
051:
052: //}}}
053:
054: /**
055: * Loads <code>dockable.xml</code> files and manages creation
056: * of new dockable windows.
057: *
058: * @see DockableWindowManager
059: *
060: * @since jEdit 4.3pre2
061: */
062: public class DockableWindowFactory {
063: //{{{ getInstance() method
064: public static synchronized DockableWindowFactory getInstance() {
065: if (instance == null)
066: instance = new DockableWindowFactory();
067: return instance;
068: } //}}}
069:
070: //{{{ DockableWindowFactory constructor
071: public DockableWindowFactory() {
072: dockableWindowFactories = new HashMap<String, Window>();
073: } //}}}
074:
075: //{{{ loadDockableWindows() method
076: /**
077: * Plugins shouldn't need to call this method.
078: * @since jEdit 4.2pre1
079: */
080: public void loadDockableWindows(PluginJAR plugin, URL uri,
081: PluginJAR.PluginCacheEntry cache) {
082: try {
083: Log.log(Log.DEBUG, DockableWindowManager.class,
084: "Loading dockables from " + uri);
085: DockableListHandler dh = new DockableListHandler(plugin,
086: uri);
087: boolean failure = XMLUtilities.parseXML(uri.openStream(),
088: dh);
089:
090: if (!failure && cache != null) {
091: cache.cachedDockableNames = dh.getCachedDockableNames();
092: cache.cachedDockableActionFlags = dh
093: .getCachedDockableActionFlags();
094: cache.cachedDockableMovableFlags = dh
095: .getCachedDockableMovableFlags();
096: }
097: } catch (IOException e) {
098: Log.log(Log.ERROR, DockableWindowManager.class, e);
099: }
100: } //}}}
101:
102: //{{{ unloadDockableWindows() method
103: /**
104: * Plugins shouldn't need to call this method.
105: * @since jEdit 4.2pre1
106: */
107: public void unloadDockableWindows(PluginJAR plugin) {
108: Iterator entries = dockableWindowFactories.entrySet()
109: .iterator();
110: while (entries.hasNext()) {
111: Map.Entry entry = (Map.Entry) entries.next();
112: Window factory = (Window) entry.getValue();
113: if (factory.plugin == plugin)
114: entries.remove();
115: }
116: } //}}}
117:
118: //{{{ cacheDockableWindows() method
119: /**
120: * @since jEdit 4.2pre1
121: */
122: public void cacheDockableWindows(PluginJAR plugin, String[] name,
123: boolean[] actions, boolean[] movable) {
124: for (int i = 0; i < name.length; i++) {
125: Window factory = new Window(plugin, name[i], null,
126: actions[i], movable[i]);
127: dockableWindowFactories.put(name[i], factory);
128: }
129: } //}}}
130:
131: //{{{ registerDockableWindow() method
132: public void registerDockableWindow(PluginJAR plugin, String name,
133: String code, boolean actions, boolean movable) {
134: Window factory = dockableWindowFactories.get(name);
135: if (factory != null) {
136: factory.code = code;
137: factory.loaded = true;
138: } else {
139: factory = new Window(plugin, name, code, actions, movable);
140: dockableWindowFactories.put(name, factory);
141: }
142: } //}}}
143:
144: //{{{ getRegisteredDockableWindows() method
145: public String[] getRegisteredDockableWindows() {
146: String[] retVal = new String[dockableWindowFactories.size()];
147: Iterator<Window> entries = dockableWindowFactories.values()
148: .iterator();
149: int i = 0;
150: while (entries.hasNext()) {
151: Window factory = entries.next();
152: retVal[i++] = factory.name;
153: }
154:
155: return retVal;
156: } //}}}
157:
158: //{{{ getDockableWindowIterator() method
159: Iterator<Window> getDockableWindowIterator() {
160: return dockableWindowFactories.values().iterator();
161: } //}}}
162:
163: //{{{ DockableListHandler class
164: class DockableListHandler extends DefaultHandler {
165: //{{{ DockableListHandler constructor
166: /**
167: * @param plugin - the pluginJAR for which we are loading the dockables.xml
168: * @param uri - the uri of the dockables.xml file?
169: */
170: DockableListHandler(PluginJAR plugin, URL uri) {
171: this .plugin = plugin;
172: this .uri = uri;
173: stateStack = new Stack();
174: actions = true;
175: movable = MOVABLE_DEFAULT;
176:
177: code = new StringBuffer();
178: cachedDockableNames = new LinkedList<String>();
179: cachedDockableActionFlags = new LinkedList();
180: cachedDockableMovableFlags = new LinkedList();
181: } //}}}
182:
183: //{{{ resolveEntity() method
184: public InputSource resolveEntity(String publicId,
185: String systemId) {
186: return XMLUtilities.findEntity(systemId, "dockables.dtd",
187: MiscUtilities.class);
188: } //}}}
189:
190: //{{{ characters() method
191: public void characters(char[] c, int off, int len) {
192: String tag = peekElement();
193: if (tag.equals("DOCKABLE"))
194: code.append(c, off, len);
195: } //}}}
196:
197: //{{{ startElement() method
198: public void startElement(String uri, String localName,
199: String qName, Attributes attrs) {
200: String tag = pushElement(qName);
201: if (tag.equals("DOCKABLE")) {
202: dockableName = attrs.getValue("NAME");
203: actions = "FALSE".equals(attrs.getValue("NO_ACTIONS"));
204: String movableAttr = attrs.getValue("MOVABLE");
205: if (movableAttr != null)
206: movable = movableAttr.equalsIgnoreCase("TRUE");
207: }
208: } //}}}
209:
210: //{{{ endElement() method
211: public void endElement(String uri, String localName, String name) {
212: if (name == null)
213: return;
214:
215: String tag = peekElement();
216:
217: if (name.equals(tag)) {
218: if (tag.equals("DOCKABLE")) {
219: registerDockableWindow(plugin, dockableName, code
220: .toString(), actions, movable);
221: cachedDockableNames.add(dockableName);
222: cachedDockableActionFlags.add(new Boolean(actions));
223: cachedDockableMovableFlags
224: .add(new Boolean(movable));
225: // make default be true for the next
226: // action
227: actions = true;
228: movable = MOVABLE_DEFAULT;
229: code.setLength(0);
230: }
231:
232: popElement();
233: } else {
234: // can't happen
235: throw new InternalError();
236: }
237: } //}}}
238:
239: //{{{ startDocument() method
240: public void startDocument() {
241: try {
242: pushElement(null);
243: } catch (Exception e) {
244: e.printStackTrace();
245: }
246: } //}}}
247:
248: //{{{ getCachedDockableNames() method
249: public String[] getCachedDockableNames() {
250: return (String[]) cachedDockableNames
251: .toArray(new String[cachedDockableNames.size()]);
252: } //}}}
253:
254: //{{{ getCachedDockableActionFlags() method
255: public boolean[] getCachedDockableActionFlags() {
256: boolean[] returnValue = new boolean[cachedDockableActionFlags
257: .size()];
258: Iterator iter = cachedDockableActionFlags.iterator();
259: int i = 0;
260: while (iter.hasNext()) {
261: boolean flag = ((Boolean) iter.next()).booleanValue();
262: returnValue[i++] = flag;
263: }
264:
265: return returnValue;
266: } //}}}
267:
268: //{{{ getCachedDockableMovableFlags() method
269: public boolean[] getCachedDockableMovableFlags() {
270: boolean[] returnValue = new boolean[cachedDockableMovableFlags
271: .size()];
272: Iterator iter = cachedDockableMovableFlags.iterator();
273: int i = 0;
274: while (iter.hasNext()) {
275: boolean flag = ((Boolean) iter.next()).booleanValue();
276: returnValue[i++] = flag;
277: }
278:
279: return returnValue;
280: } //}}}
281:
282: //{{{ Private members
283:
284: //{{{ Instance variables
285: private PluginJAR plugin;
286: // What is the purpose of this?
287: private URL uri;
288:
289: private java.util.List<String> cachedDockableNames;
290: private java.util.List cachedDockableActionFlags;
291: private java.util.List cachedDockableMovableFlags;
292:
293: private String dockableName;
294: private StringBuffer code;
295: private boolean actions;
296: private boolean movable;
297: final boolean MOVABLE_DEFAULT = false;
298:
299: private Stack stateStack;
300:
301: //}}}
302:
303: //{{{ pushElement() method
304: private String pushElement(String name) {
305: name = (name == null) ? null : name.intern();
306:
307: stateStack.push(name);
308:
309: return name;
310: } //}}}
311:
312: //{{{ peekElement() method
313: private String peekElement() {
314: return (String) stateStack.peek();
315: } //}}}
316:
317: //{{{ popElement() method
318: private String popElement() {
319: return (String) stateStack.pop();
320: } //}}}
321:
322: //}}}
323: } //}}}
324:
325: //{{{ Window class
326: class Window {
327: PluginJAR plugin;
328: String name;
329: String code;
330: boolean loaded;
331: boolean movable;
332:
333: //{{{ Window constructor
334: Window(PluginJAR plugin, String name, String code,
335: boolean actions, boolean movable) {
336: this .plugin = plugin;
337: this .name = name;
338: this .code = code;
339: this .movable = movable;
340:
341: if (code != null)
342: loaded = true;
343:
344: if (actions) {
345: ActionSet actionSet = (plugin == null ? jEdit
346: .getBuiltInActionSet() : plugin.getActionSet());
347: actionSet.addAction(new OpenAction(name));
348: actionSet.addAction(new ToggleAction(name));
349: actionSet.addAction(new FloatAction(name));
350:
351: String label = jEdit.getProperty(name + ".label");
352: if (label == null)
353: label = "NO LABEL PROPERTY: " + name;
354:
355: String[] args = { label };
356: jEdit.setTemporaryProperty(name + ".label", label);
357: jEdit.setTemporaryProperty(name + "-toggle.label",
358: jEdit.getProperty("view.docking.toggle.label",
359: args));
360: jEdit.setTemporaryProperty(name + "-toggle.toggle",
361: "true");
362: jEdit.setTemporaryProperty(name + "-float.label", jEdit
363: .getProperty("view.docking.float.label", args));
364: }
365: } //}}}
366:
367: //{{{ load() method
368: void load() {
369: if (loaded)
370: return;
371:
372: loadDockableWindows(plugin, plugin.getDockablesURI(), null);
373: } //}}}
374:
375: //{{{ createDockableWindow() method
376: JComponent createDockableWindow(View view, String position) {
377: load();
378:
379: if (!loaded) {
380: Log.log(Log.WARNING, this , "Outdated cache");
381: return null;
382: }
383:
384: NameSpace nameSpace = new NameSpace(BeanShell
385: .getNameSpace(), "DockableWindowManager.Factory"
386: + ".createDockableWindow()");
387: try {
388: nameSpace.setVariable("position", position);
389: } catch (UtilEvalError e) {
390: Log.log(Log.ERROR, this , e);
391: }
392: JComponent win = (JComponent) BeanShell.eval(view,
393: nameSpace, code);
394: return win;
395: } //}}}
396:
397: //{{{ OpenAction class
398: class OpenAction extends EditAction {
399: private String dockable;
400:
401: //{{{ OpenAction constructor
402: OpenAction(String name) {
403: super (name);
404: this .dockable = name;
405: } //}}}
406:
407: //{{{ invoke() method
408: public void invoke(View view) {
409: view.getDockableWindowManager().showDockableWindow(
410: dockable);
411: } //}}}
412:
413: //{{{ getCode() method
414: public String getCode() {
415: return "view.getDockableWindowManager()"
416: + ".showDockableWindow(\"" + dockable + "\");";
417: } //}}}
418: } //}}}
419:
420: //{{{ ToggleAction class
421: class ToggleAction extends EditAction {
422: private String dockable;
423:
424: //{{{ ToggleAction constructor
425: ToggleAction(String name) {
426: super (name + "-toggle");
427: this .dockable = name;
428: } //}}}
429:
430: //{{{ invoke() method
431: public void invoke(View view) {
432: view.getDockableWindowManager().toggleDockableWindow(
433: dockable);
434: } //}}}
435:
436: //{{{ isSelected() method
437: public boolean isSelected(View view) {
438: return view.getDockableWindowManager()
439: .isDockableWindowVisible(dockable);
440: } //}}}
441:
442: //{{{ getCode() method
443: public String getCode() {
444: return "view.getDockableWindowManager()"
445: + ".toggleDockableWindow(\"" + dockable
446: + "\");";
447: } //}}}
448: } //}}}
449:
450: //{{{ FloatAction class
451: class FloatAction extends EditAction {
452: private String dockable;
453:
454: //{{{ FloatAction constructor
455: FloatAction(String name) {
456: super (name + "-float");
457: this .dockable = name;
458: } //}}}
459:
460: //{{{ invoke() method
461: public void invoke(View view) {
462: view.getDockableWindowManager().floatDockableWindow(
463: dockable);
464: } //}}}
465:
466: //{{{ getCode() method
467: public String getCode() {
468: return "view.getDockableWindowManager()"
469: + ".floatDockableWindow(\"" + dockable + "\");";
470: } //}}}
471: } //}}}
472: } //}}}
473:
474: private static DockableWindowFactory instance;
475: private HashMap<String, Window> dockableWindowFactories;
476: }
|