001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.core;
043:
044: import java.io.IOException;
045: import java.util.Collection;
046: import java.util.Enumeration;
047: import java.util.LinkedList;
048: import java.util.logging.Level;
049: import java.util.logging.Logger;
050: import javax.swing.Action;
051: import javax.swing.KeyStroke;
052: import javax.swing.text.Keymap;
053: import org.netbeans.core.NbKeymap.KeymapAction;
054: import org.openide.cookies.InstanceCookie;
055: import org.openide.filesystems.FileAttributeEvent;
056: import org.openide.filesystems.FileChangeAdapter;
057: import org.openide.filesystems.FileEvent;
058: import org.openide.filesystems.FileObject;
059: import org.openide.filesystems.Repository;
060: import org.openide.loaders.DataFolder;
061: import org.openide.loaders.DataObject;
062: import org.openide.util.Exceptions;
063: import org.openide.util.Lookup;
064: import org.openide.util.RequestProcessor;
065: import org.openide.util.Utilities;
066:
067: /**
068: * Bridge to old layers based system.
069: *
070: * @author Jan Jancura
071: */
072: class ShortcutsFolder {
073:
074: private static final String PROFILES_FOLDER = "Keymaps";
075: private static final String SHORTCUTS_FOLDER = "Shortcuts";
076: private static final String CURRENT_PROFILE_ATTRIBUTE = "currentKeymap";
077:
078: private static ShortcutsFolder shortcutsFolder;
079: private Listener listener = new Listener();
080: private FileObject profilesFileObject;
081: private FileObject shortcutsFileObject;
082: private FileObject currentFolder;
083: private Logger debug = Logger.getLogger(ShortcutsFolder.class
084: .getName());
085: private Collection<DataObject> dataObjects;
086:
087: static void initShortcuts() {
088: if (shortcutsFolder != null)
089: return;
090: shortcutsFolder = new ShortcutsFolder();
091: }
092:
093: private ShortcutsFolder() {
094: try {
095: FileObject root = Repository.getDefault()
096: .getDefaultFileSystem().getRoot();
097: profilesFileObject = root.getFileObject(PROFILES_FOLDER);
098: if (profilesFileObject == null)
099: profilesFileObject = root.createFolder(PROFILES_FOLDER);
100: profilesFileObject.addFileChangeListener(listener);
101:
102: shortcutsFileObject = root.getFileObject(SHORTCUTS_FOLDER);
103: if (shortcutsFileObject == null)
104: shortcutsFileObject = root
105: .createFolder(SHORTCUTS_FOLDER);
106: shortcutsFileObject.addFileChangeListener(listener);
107: } catch (IOException ex) {
108: Exceptions.printStackTrace(ex);
109: }
110: refresh();
111: }
112:
113: static void waitFinished() {
114: shortcutsFolder.listener.task.waitFinished();
115: }
116:
117: private void refresh() {
118:
119: // get keymap and delete old shortcuts
120: NbKeymap keymap = (NbKeymap) Lookup.getDefault().lookup(
121: Keymap.class);
122: keymap.removeBindings();
123: dataObjects = new LinkedList<DataObject>();
124:
125: // update main shortcuts
126: readShortcuts(keymap, shortcutsFileObject);
127:
128: // update shortcuts from profile
129: String keymapName = (String) profilesFileObject
130: .getAttribute(CURRENT_PROFILE_ATTRIBUTE);
131: if (keymapName == null || "".equals(keymapName))
132: keymapName = "NetBeans"; // NOI18N
133: if (currentFolder != null)
134: currentFolder.removeFileChangeListener(listener);
135: currentFolder = Repository.getDefault().getDefaultFileSystem()
136: .getRoot().getFileObject(
137: PROFILES_FOLDER + '/' + keymapName);
138: if (currentFolder == null) {
139: try {
140: currentFolder = profilesFileObject
141: .createFolder(keymapName);
142: } catch (IOException ioe) {
143: Exceptions.printStackTrace(ioe);
144: }
145: }
146: if (currentFolder != null) {
147: readShortcuts(keymap, currentFolder);
148: // add listener to current profile folder
149: currentFolder.addFileChangeListener(listener);
150: }
151: }
152:
153: private void readShortcuts(NbKeymap keymap, FileObject fileObject) {
154: debug.fine("\nreadShortcuts " + fileObject);
155: DataFolder folder = DataFolder.findFolder(fileObject);
156: Enumeration en = folder.children(false);
157: while (en.hasMoreElements()) {
158: DataObject dataObject = (DataObject) en.nextElement();
159: if (dataObject instanceof DataFolder)
160: continue;
161: InstanceCookie ic = dataObject
162: .getCookie(InstanceCookie.class);
163: if (ic == null)
164: continue;
165: try {
166: Action action = (Action) ic.instanceCreate();
167: String shortcuts = dataObject.getName();
168: debug.fine(" " + shortcuts + " : " + action);
169: KeyStroke[] keyStrokes = Utilities
170: .stringToKeys(shortcuts);
171: if (keyStrokes != null) {
172: addShortcut(keymap, action, keyStrokes);
173: } else { // see e.g. secondary exception in #74169
174: debug.warning("Unrecognized shortcut name from "
175: + dataObject.getPrimaryFile().getPath()); // NOI18N
176: }
177: //remember DataObjects used to create the Actions so that there are
178: //the same Action instances in the menu
179: dataObjects.add(dataObject);
180: } catch (ClassNotFoundException x) {
181: Logger.getLogger(ShortcutsFolder.class.getName()).log(
182: Level.WARNING,
183: "{0} ignored; cannot load class {1}",
184: new Object[] {
185: dataObject.getPrimaryFile().getPath(),
186: ic.instanceName() });
187: } catch (Exception ex) {
188: Logger.getLogger(ShortcutsFolder.class.getName()).log(
189: Level.WARNING, null, ex);
190: }
191: }
192: }
193:
194: private static void addShortcut(NbKeymap keymap, Action action,
195: KeyStroke[] keyStrokes) {
196: Keymap currentKeymap = keymap;
197: int i, k = keyStrokes.length - 1;
198: for (i = 0; i < k; i++) {
199: Action a = currentKeymap.getAction(keyStrokes[i]);
200: if (a == null) {
201: a = keymap.createMapAction(
202: new NbKeymap.SubKeymap(null), keyStrokes[i]);
203: currentKeymap.addActionForKeyStroke(keyStrokes[i], a);
204: }
205: if (!(a instanceof KeymapAction))
206: return;
207: currentKeymap = ((KeymapAction) a).getSubMap();
208: }
209: currentKeymap.addActionForKeyStroke(keyStrokes[k], action);
210: }
211:
212: private class Listener extends FileChangeAdapter implements
213: Runnable {
214:
215: private RequestProcessor.Task task = new RequestProcessor(
216: "ShortcutsFolder").create(this );
217:
218: public void run() {
219: refresh();
220: }
221:
222: @Override
223: public void fileDataCreated(FileEvent fe) {
224: task.schedule(500);
225: }
226:
227: @Override
228: public void fileChanged(FileEvent fe) {
229: task.schedule(500);
230: }
231:
232: @Override
233: public void fileDeleted(FileEvent fe) {
234: task.schedule(500);
235: }
236:
237: @Override
238: public void fileAttributeChanged(FileAttributeEvent fe) {
239: if (fe.getName() != null
240: && !CURRENT_PROFILE_ATTRIBUTE.equals(fe.getName()))
241: return;
242: task.schedule(500);
243: }
244: }
245: }
|