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.modules.options.keymap;
043:
044: import java.awt.Component;
045: import java.awt.Rectangle;
046: import java.awt.event.ActionEvent;
047: import java.awt.event.ActionListener;
048: import java.util.Iterator;
049: import java.util.List;
050: import javax.swing.AbstractButton;
051: import javax.swing.AbstractListModel;
052: import javax.swing.DefaultListModel;
053: import javax.swing.JLabel;
054: import javax.swing.JPanel;
055: import javax.swing.SwingUtilities;
056: import javax.swing.event.ListSelectionEvent;
057: import javax.swing.event.ListSelectionListener;
058: import javax.swing.event.TreeSelectionEvent;
059: import javax.swing.event.TreeSelectionListener;
060: import javax.swing.tree.TreePath;
061: import org.netbeans.core.options.keymap.api.ShortcutAction;
062: import org.openide.DialogDisplayer;
063: import org.openide.NotifyDescriptor;
064: import org.openide.NotifyDescriptor.InputLine;
065: import org.openide.NotifyDescriptor.Message;
066: import org.openide.awt.Mnemonics;
067: import org.openide.util.NbBundle;
068:
069: /**
070: *
071: * @author Jan Jancura
072: */
073: public class KeymapPanel extends JPanel implements ActionListener,
074: TreeSelectionListener, ListSelectionListener {
075:
076: private boolean listen = false;
077:
078: /** Creates new form KeymapPanel1 */
079: public KeymapPanel() {
080: initComponents();
081: loc(bDuplicate, "Duplicate");
082: loc(bDelete, "Delete");
083: // loc (rbAction, "Show_Actions");
084: // loc (rbShortcut, "Show_Shortcuts");
085: liShortcuts.getAccessibleContext().setAccessibleName(
086: loc("AN_Shortcuts"));
087: liShortcuts.getAccessibleContext().setAccessibleDescription(
088: loc("AD_Shortcuts"));
089: tActions.getAccessibleContext().setAccessibleName(
090: loc("AN_Actions"));
091: tActions.getAccessibleContext().setAccessibleDescription(
092: loc("AD_Actions"));
093: cbProfile.getAccessibleContext().setAccessibleName(
094: loc("AN_Profiles"));
095: cbProfile.getAccessibleContext().setAccessibleDescription(
096: loc("AD_Profiles"));
097: // bgViewAs.add (rbAction);
098: // bgViewAs.add (rbShortcut);
099: bDuplicate.addActionListener(this );
100: bDelete.addActionListener(this );
101: // rbAction.setSelected (true);
102: // rbAction.addActionListener (this);
103: // rbShortcut.addActionListener (this);
104: tActions.setRootVisible(false);
105: tActions.setShowsRootHandles(true);
106: tActions.addTreeSelectionListener(this );
107: cbProfile.addActionListener(this );
108: loc(bAdd, "Add_Shortcut");
109: loc(bRemove, "Remove_Shortcut");
110: bAdd.addActionListener(this );
111: bRemove.addActionListener(this );
112: liShortcuts.addListSelectionListener(this );
113: bAdd.setEnabled(false);
114: bRemove.setEnabled(false);
115: loc(lShortcuts, "Shortcuts"); // NOI18N
116: lShortcuts.setLabelFor(liShortcuts);
117: loc(lActions, "Actions");
118: lActions.setLabelFor(tActions);
119: }
120:
121: /** This method is called from within the constructor to
122: * initialize the form.
123: * WARNING: Do NOT modify this code. The content of this method is
124: * always regenerated by the Form Editor.
125: */
126: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
127: private void initComponents() {
128: lProfile = new javax.swing.JLabel();
129: cbProfile = new javax.swing.JComboBox();
130: bDuplicate = new javax.swing.JButton();
131: bDelete = new javax.swing.JButton();
132: lActions = new javax.swing.JLabel();
133: spActions = new javax.swing.JScrollPane();
134: tActions = new javax.swing.JTree();
135: lShortcuts = new javax.swing.JLabel();
136: spShortcuts = new javax.swing.JScrollPane();
137: liShortcuts = new javax.swing.JList();
138: bAdd = new javax.swing.JButton();
139: bRemove = new javax.swing.JButton();
140:
141: lProfile.setText("Profile:");
142:
143: bDuplicate.setText("Duplicate...");
144:
145: bDelete.setText("Delete");
146:
147: lActions.setText("Actions:");
148:
149: spActions.setViewportView(tActions);
150:
151: lShortcuts.setText("Shortcuts:");
152:
153: spShortcuts.setViewportView(liShortcuts);
154:
155: bAdd.setText("Add...");
156:
157: bRemove.setText("Remove");
158:
159: org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(
160: this );
161: this .setLayout(layout);
162: layout
163: .setHorizontalGroup(layout
164: .createParallelGroup(
165: org.jdesktop.layout.GroupLayout.LEADING)
166: .add(
167: layout
168: .createSequentialGroup()
169: .add(
170: layout
171: .createParallelGroup(
172: org.jdesktop.layout.GroupLayout.LEADING)
173: .add(
174: layout
175: .createSequentialGroup()
176: .add(
177: lProfile)
178: .addPreferredGap(
179: org.jdesktop.layout.LayoutStyle.RELATED)
180: .add(
181: cbProfile,
182: 0,
183: 139,
184: Short.MAX_VALUE)
185: .addPreferredGap(
186: org.jdesktop.layout.LayoutStyle.RELATED)
187: .add(
188: bDuplicate))
189: .add(
190: layout
191: .createSequentialGroup()
192: .add(
193: spShortcuts,
194: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
195: 175,
196: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
197: .addPreferredGap(
198: org.jdesktop.layout.LayoutStyle.RELATED)
199: .add(
200: layout
201: .createParallelGroup(
202: org.jdesktop.layout.GroupLayout.LEADING)
203: .add(
204: bRemove)
205: .add(
206: bAdd))))
207: .addPreferredGap(
208: org.jdesktop.layout.LayoutStyle.RELATED)
209: .add(bDelete))
210: .add(
211: layout.createSequentialGroup().add(
212: lActions).addContainerGap())
213: .add(
214: spActions,
215: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
216: 367, Short.MAX_VALUE).add(
217: layout.createSequentialGroup().add(
218: lShortcuts).addContainerGap()));
219:
220: layout.linkSize(new java.awt.Component[] { bAdd, bRemove },
221: org.jdesktop.layout.GroupLayout.HORIZONTAL);
222:
223: layout.linkSize(
224: new java.awt.Component[] { bDelete, bDuplicate },
225: org.jdesktop.layout.GroupLayout.HORIZONTAL);
226:
227: layout
228: .setVerticalGroup(layout
229: .createParallelGroup(
230: org.jdesktop.layout.GroupLayout.LEADING)
231: .add(
232: layout
233: .createSequentialGroup()
234: .add(
235: layout
236: .createParallelGroup(
237: org.jdesktop.layout.GroupLayout.BASELINE)
238: .add(bDelete)
239: .add(bDuplicate)
240: .add(lProfile)
241: .add(
242: cbProfile,
243: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
244: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
245: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
246: .addPreferredGap(
247: org.jdesktop.layout.LayoutStyle.RELATED)
248: .add(lActions)
249: .addPreferredGap(
250: org.jdesktop.layout.LayoutStyle.RELATED)
251: .add(
252: spActions,
253: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
254: 168, Short.MAX_VALUE)
255: .addPreferredGap(
256: org.jdesktop.layout.LayoutStyle.RELATED)
257: .add(lShortcuts)
258: .addPreferredGap(
259: org.jdesktop.layout.LayoutStyle.RELATED)
260: .add(
261: layout
262: .createParallelGroup(
263: org.jdesktop.layout.GroupLayout.LEADING,
264: false)
265: .add(
266: layout
267: .createSequentialGroup()
268: .add(
269: bAdd)
270: .addPreferredGap(
271: org.jdesktop.layout.LayoutStyle.RELATED)
272: .add(
273: bRemove))
274: .add(
275: spShortcuts,
276: 0,
277: 52,
278: Short.MAX_VALUE))));
279: }// </editor-fold>//GEN-END:initComponents
280:
281: // Variables declaration - do not modify//GEN-BEGIN:variables
282: private javax.swing.JButton bAdd;
283: private javax.swing.JButton bDelete;
284: private javax.swing.JButton bDuplicate;
285: private javax.swing.JButton bRemove;
286: private javax.swing.JComboBox cbProfile;
287: private javax.swing.JLabel lActions;
288: private javax.swing.JLabel lProfile;
289: private javax.swing.JLabel lShortcuts;
290: private javax.swing.JList liShortcuts;
291: private javax.swing.JScrollPane spActions;
292: private javax.swing.JScrollPane spShortcuts;
293: private javax.swing.JTree tActions;
294:
295: // End of variables declaration//GEN-END:variables
296:
297: public void actionPerformed(ActionEvent e) {
298: if (!listen)
299: return;
300: Object source = e.getSource();
301: if (source == bAdd) {
302: Object action = tActions.getSelectionPath()
303: .getLastPathComponent();
304: String shortcut = getModel().showShortcutsDialog();
305: if (shortcut == null)
306: return;
307: getModel().addShortcut(tActions.getSelectionPath(),
308: shortcut);
309: selectAction(action);
310: if (liShortcuts.getModel().getSize() > 0)
311: liShortcuts.setSelectedIndex(0);
312: SwingUtilities.invokeLater(new Runnable() {
313: public void run() {
314: liShortcuts.requestFocus();
315: }
316: });
317: } else if (source == bRemove) {
318: int index = liShortcuts.getSelectedIndex();
319: Object action = tActions.getSelectionPath()
320: .getLastPathComponent();
321: String shortcut = (String) liShortcuts.getSelectedValue();
322: getModel().removeShortcut(tActions.getSelectionPath(),
323: shortcut);
324: selectAction(action);
325: if (liShortcuts.getModel().getSize() > index)
326: liShortcuts.setSelectedIndex(index);
327: else if (liShortcuts.getModel().getSize() > 0)
328: liShortcuts.setSelectedIndex(0);
329: } else if (source == bDelete) {
330: deleteCurrentProfile();
331: } else if (source == cbProfile) {
332: String profile = (String) cbProfile.getSelectedItem();
333: final TreePath tp = tActions.getSelectionPath();
334: final boolean expanded = tActions.isExpanded(tp);
335: getModel().setCurrentProfile(profile);
336:
337: SwingUtilities.invokeLater(new Runnable() { //rememebrer jTree state
338: public void run() {
339: if (expanded)
340: tActions.expandPath(tp);
341: tActions.setSelectionPath(tp);
342: tActions.scrollPathToVisible(tp);
343: }
344: });
345:
346: if (getModel().isCustomProfile(profile))
347: loc(bDelete, "Delete"); // NOI18N
348: else
349: loc(bDelete, "Restore"); // NOI18N
350: refreshAction();
351: } else if (source == bDuplicate) {
352: InputLine il = new InputLine(
353: loc("CTL_Create_New_Profile_Message"), // NOI18N
354: loc("CTL_Create_New_Profile_Title") // NOI18N
355: );
356: il.setInputText((String) cbProfile.getSelectedItem());
357: DialogDisplayer.getDefault().notify(il);
358: if (il.getValue() == NotifyDescriptor.OK_OPTION) {
359: String newProfile = il.getInputText();
360: Iterator it = getModel().getProfiles().iterator();
361: while (it.hasNext())
362: if (newProfile.equals(it.next())) {
363: Message md = new Message(
364: loc("CTL_Duplicate_Profile_Name"), // NOI18N
365: Message.ERROR_MESSAGE);
366: DialogDisplayer.getDefault().notify(md);
367: return;
368: }
369: getModel().cloneProfile(newProfile);
370: cbProfile.addItem(il.getInputText());
371: cbProfile.setSelectedItem(il.getInputText());
372: }
373: return;
374: }
375: }
376:
377: public void valueChanged(TreeSelectionEvent e) {
378: if (!listen)
379: return;
380: refreshAction();
381: }
382:
383: public void valueChanged(ListSelectionEvent e) {
384: if (!listen)
385: return;
386: // selected shourtcut changed
387: int i = liShortcuts.getSelectedIndex();
388: if (i < 0) {
389: bRemove.setEnabled(false);
390: return;
391: }
392: bRemove.setEnabled(true);
393: }
394:
395: private boolean initialized = false;
396:
397: void update() {
398: if (!initialized) {
399: initialized = true;
400: listen = false;
401:
402: tActions
403: .setCellRenderer(new KeymapListRenderer(getModel()));
404: tActions.setModel(getModel());
405:
406: // cbProfile
407: List keymaps = getModel().getProfiles();
408: cbProfile.removeAllItems();
409: int i, k = keymaps.size();
410: for (i = 0; i < k; i++)
411: cbProfile.addItem(keymaps.get(i));
412: listen = true;
413: }
414: cbProfile.setSelectedItem(getModel().getCurrentProfile());
415: }
416:
417: private void deleteCurrentProfile() {
418: String currentProfile = (String) cbProfile.getSelectedItem();
419: getModel().deleteProfile(currentProfile);
420: if (getModel().isCustomProfile(currentProfile)) {
421: cbProfile.removeItem(currentProfile);
422: cbProfile.setSelectedIndex(0);
423: }
424: }
425:
426: void applyChanges() {
427: if (!initialized)
428: return; // not initialized yet.
429: getModel().apply();
430: }
431:
432: void cancel() {
433: if (model == null)
434: return;
435: model.cancel();
436: }
437:
438: boolean dataValid() {
439: return true;
440: }
441:
442: boolean isChanged() {
443: return getModel().isChanged();
444: }
445:
446: private KeymapViewModel model;
447:
448: synchronized KeymapViewModel getModel() {
449: if (model == null)
450: model = new KeymapViewModel();
451: return model;
452: }
453:
454: // other methods ...........................................................
455:
456: private static String loc(String key) {
457: return NbBundle.getMessage(KeymapPanel.class, key);
458: }
459:
460: private static void loc(Component c, String key) {
461: if (!(c instanceof JLabel)) {
462: c.getAccessibleContext()
463: .setAccessibleName(loc("AN_" + key));
464: c.getAccessibleContext().setAccessibleDescription(
465: loc("AD_" + key));
466: }
467: if (c instanceof AbstractButton) {
468: Mnemonics.setLocalizedText((AbstractButton) c, loc("CTL_"
469: + key));
470: } else {
471: Mnemonics.setLocalizedText((JLabel) c, loc("CTL_" + key));
472: }
473: }
474:
475: void refreshAction() {
476: Object action = tActions.getSelectionPath() == null ? null
477: : tActions.getSelectionPath().getLastPathComponent();
478: selectAction(action);
479: if (liShortcuts.getModel().getSize() > 0)
480: liShortcuts.setSelectedIndex(0);
481: }
482:
483: void selectAction(Object action) {
484: if (action == null || action instanceof String) {
485: liShortcuts.setModel(new DefaultListModel());
486: bAdd.setEnabled(false);
487: bRemove.setEnabled(false);
488: return;
489: }
490: bAdd.setEnabled(true);
491: bRemove.setEnabled(false);
492: final String[] shortcuts = getModel().getShortcuts(
493: (ShortcutAction) action);
494: liShortcuts.setModel(new AbstractListModel() {
495: public int getSize() {
496: return shortcuts.length;
497: }
498:
499: public Object getElementAt(int i) {
500: return shortcuts[i];
501: }
502: });
503: }
504: }
|