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-2007 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.java.hints.options;
043:
044: import java.awt.BorderLayout;
045: import java.awt.Point;
046: import java.awt.Rectangle;
047: import java.awt.event.ActionEvent;
048: import java.awt.event.ActionListener;
049: import java.awt.event.KeyEvent;
050: import java.awt.event.KeyListener;
051: import java.awt.event.MouseEvent;
052: import java.awt.event.MouseListener;
053: import java.util.HashMap;
054: import java.util.Map;
055: import java.util.prefs.AbstractPreferences;
056: import java.util.prefs.BackingStoreException;
057: import java.util.prefs.Preferences;
058: import javax.swing.JCheckBox;
059: import javax.swing.JComboBox;
060: import javax.swing.JComponent;
061: import javax.swing.JEditorPane;
062: import javax.swing.JPanel;
063: import javax.swing.JTree;
064: import javax.swing.event.ChangeEvent;
065: import javax.swing.event.ChangeListener;
066: import javax.swing.event.TreeSelectionEvent;
067: import javax.swing.event.TreeSelectionListener;
068: import javax.swing.tree.DefaultMutableTreeNode;
069: import javax.swing.tree.DefaultTreeModel;
070: import javax.swing.tree.TreePath;
071:
072: import org.openide.filesystems.FileObject;
073: import org.openide.util.Exceptions;
074:
075: import org.netbeans.modules.java.hints.spi.AbstractHint;
076: import static org.netbeans.modules.java.hints.spi.AbstractHint.*;
077:
078: /** Contains all important listeners and logic of the Hints Panel.
079: *
080: * @author Petr Hrebejk
081: */
082: class HintsPanelLogic implements MouseListener, KeyListener,
083: TreeSelectionListener, ChangeListener, ActionListener {
084:
085: private Map<AbstractHint, ModifiedPreferences> changes;
086:
087: private static Map<HintSeverity, Integer> severity2index;
088:
089: private static final String DESCRIPTION_HEADER = "<html><head>" + // NOI18N
090: //"<link rel=\"StyleSheet\" href=\"nbdocs://org.netbeans.modules.usersguide/org/netbeans/modules/usersguide/ide.css\" type=\"text/css\">" // NOI18N
091: //"<link rel=\"StyleSheet\" href=\"nbresloc:/org/netbeans/modules/java/hints/resources/ide.css\" type=\"text/css\">" + // NOI18N
092: "</head><body>"; // NOI18N
093:
094: private static final String DESCRIPTION_FOOTER = "</body></html>"; // NOI18N
095:
096: static {
097: severity2index = new HashMap<HintSeverity, Integer>();
098: severity2index.put(HintSeverity.ERROR, 0);
099: severity2index.put(HintSeverity.WARNING, 1);
100: severity2index.put(HintSeverity.CURRENT_LINE_WARNING, 2);
101: }
102:
103: private JTree errorTree;
104: private JComboBox severityComboBox;
105: private JCheckBox tasklistCheckBox;
106: private JPanel customizerPanel;
107: private JEditorPane descriptionTextArea;
108:
109: HintsPanelLogic() {
110: changes = new HashMap<AbstractHint, ModifiedPreferences>();
111: }
112:
113: void connect(JTree errorTree, JComboBox severityComboBox,
114: JCheckBox tasklistCheckBox, JPanel customizerPanel,
115: JEditorPane descriptionTextArea) {
116:
117: this .errorTree = errorTree;
118: this .severityComboBox = severityComboBox;
119: this .tasklistCheckBox = tasklistCheckBox;
120: this .customizerPanel = customizerPanel;
121: this .descriptionTextArea = descriptionTextArea;
122:
123: valueChanged(null);
124:
125: errorTree.addKeyListener(this );
126: errorTree.addMouseListener(this );
127: errorTree.getSelectionModel().addTreeSelectionListener(this );
128:
129: severityComboBox.addActionListener(this );
130: tasklistCheckBox.addChangeListener(this );
131:
132: }
133:
134: void disconnect() {
135:
136: errorTree.removeKeyListener(this );
137: errorTree.removeMouseListener(this );
138: errorTree.getSelectionModel().removeTreeSelectionListener(this );
139:
140: severityComboBox.removeActionListener(this );
141: tasklistCheckBox.removeChangeListener(this );
142:
143: componentsSetEnabled(false);
144: }
145:
146: synchronized void applyChanges() {
147: for (AbstractHint hint : changes.keySet()) {
148: ModifiedPreferences mn = changes.get(hint);
149: mn.store(hint.getPreferences(HintsSettings
150: .getCurrentProfileId()));
151: }
152: }
153:
154: /** Were there any changes in the settings
155: */
156: boolean isChanged() {
157: return !changes.isEmpty();
158: }
159:
160: synchronized Preferences getCurrentPrefernces(AbstractHint hint) {
161: Preferences node = changes.get(hint);
162: return node == null ? hint.getPreferences(HintsSettings
163: .getCurrentProfileId()) : node;
164: }
165:
166: synchronized Preferences getPreferences4Modification(
167: AbstractHint hint) {
168: Preferences node = changes.get(hint);
169: if (node == null) {
170: node = new ModifiedPreferences(
171: hint.getPreferences(HintsSettings
172: .getCurrentProfileId()));
173: changes.put(hint, (ModifiedPreferences) node);
174: }
175: return node;
176: }
177:
178: static Object getUserObject(TreePath path) {
179: if (path == null)
180: return null;
181: DefaultMutableTreeNode tn = (DefaultMutableTreeNode) path
182: .getLastPathComponent();
183: return tn.getUserObject();
184: }
185:
186: static Object getUserObject(DefaultMutableTreeNode node) {
187: return node.getUserObject();
188: }
189:
190: boolean isSelected(DefaultMutableTreeNode node) {
191: for (int i = 0; i < node.getChildCount(); i++) {
192: DefaultMutableTreeNode ch = (DefaultMutableTreeNode) node
193: .getChildAt(i);
194: Object o = ch.getUserObject();
195: if (o instanceof AbstractHint) {
196: AbstractHint hint = (AbstractHint) o;
197: if (HintsSettings.isEnabled(hint,
198: getCurrentPrefernces(hint))) {
199: return true;
200: }
201: }
202: }
203: return false;
204: }
205:
206: // MouseListener implementation --------------------------------------------
207:
208: public void mouseClicked(MouseEvent e) {
209: Point p = e.getPoint();
210: TreePath path = errorTree.getPathForLocation(e.getPoint().x, e
211: .getPoint().y);
212: if (path != null) {
213: Rectangle r = errorTree.getPathBounds(path);
214: if (r != null) {
215: r.width = r.height;
216: if (r.contains(p)) {
217: toggle(path);
218: }
219: }
220: }
221: }
222:
223: public void mouseEntered(MouseEvent e) {
224: }
225:
226: public void mouseExited(MouseEvent e) {
227: }
228:
229: public void mousePressed(MouseEvent e) {
230: }
231:
232: public void mouseReleased(MouseEvent e) {
233: }
234:
235: // KeyListener implementation ----------------------------------------------
236:
237: public void keyTyped(KeyEvent e) {
238: }
239:
240: public void keyReleased(KeyEvent e) {
241: }
242:
243: public void keyPressed(KeyEvent e) {
244: if (e.getKeyCode() == KeyEvent.VK_SPACE
245: || e.getKeyCode() == KeyEvent.VK_ENTER) {
246:
247: if (e.getSource() instanceof JTree) {
248: JTree tree = (JTree) e.getSource();
249: TreePath path = tree.getSelectionPath();
250:
251: if (toggle(path)) {
252: e.consume();
253: }
254: }
255: }
256: }
257:
258: // TreeSelectionListener implementation ------------------------------------
259:
260: public void valueChanged(TreeSelectionEvent ex) {
261: Object o = getUserObject(errorTree.getSelectionPath());
262:
263: if (o instanceof AbstractHint) {
264: AbstractHint hint = (AbstractHint) o;
265:
266: // Enable components
267: componentsSetEnabled(true);
268:
269: // Set proper values to the componetnts
270:
271: Preferences p = getCurrentPrefernces(hint);
272:
273: HintSeverity severity = HintsSettings.getSeverity(hint, p);
274: if (severity != null) {
275: severityComboBox.setSelectedIndex(severity2index
276: .get(severity));
277: severityComboBox.setEnabled(true);
278: } else {
279: severityComboBox.setSelectedIndex(severity2index
280: .get(HintSeverity.ERROR));
281: severityComboBox.setEnabled(false);
282: }
283:
284: boolean toTasklist = HintsSettings
285: .isShowInTaskList(hint, p);
286: tasklistCheckBox.setSelected(toTasklist);
287:
288: String description = hint.getDescription();
289: descriptionTextArea.setText(description == null ? ""
290: : wrapDescription(description)); // NOI18N
291:
292: // Optionally show the customizer
293: customizerPanel.removeAll();
294: JComponent c = hint
295: .getCustomizer(ex == null ? getCurrentPrefernces(hint)
296: : getPreferences4Modification(hint));
297:
298: if (c != null) {
299: customizerPanel.add(c, BorderLayout.CENTER);
300: }
301: customizerPanel.getParent().invalidate();
302: ((JComponent) customizerPanel.getParent()).revalidate();
303: customizerPanel.getParent().repaint();
304: } else { // Category or nonsense selected.
305: componentsSetEnabled(false);
306: }
307: }
308:
309: // ActionListener implementation -------------------------------------------
310:
311: public void actionPerformed(ActionEvent e) {
312: if (errorTree.getSelectionPath() == null)
313: return;
314:
315: Object o = getUserObject(errorTree.getSelectionPath());
316:
317: if (o instanceof AbstractHint) {
318: AbstractHint hint = (AbstractHint) o;
319: Preferences p = getPreferences4Modification(hint);
320:
321: if (HintsSettings.getSeverity(hint, p) != null
322: && severityComboBox.equals(e.getSource()))
323: HintsSettings.setSeverity(p,
324: index2severity(severityComboBox
325: .getSelectedIndex()));
326: }
327: }
328:
329: // ChangeListener implementation -------------------------------------------
330:
331: public void stateChanged(ChangeEvent e) {
332: // System.out.println("Task list box changed ");
333: }
334:
335: // Private methods ---------------------------------------------------------
336:
337: private String wrapDescription(String description) {
338: return new StringBuffer(DESCRIPTION_HEADER).append(description)
339: .append(DESCRIPTION_FOOTER).toString();
340: }
341:
342: private HintSeverity index2severity(int index) {
343: for (Map.Entry<HintSeverity, Integer> e : severity2index
344: .entrySet()) {
345: if (e.getValue() == index) {
346: return e.getKey();
347: }
348: }
349: throw new IllegalStateException("Unknown severity");
350: }
351:
352: private boolean toggle(TreePath treePath) {
353:
354: if (treePath == null)
355: return false;
356:
357: Object o = getUserObject(treePath);
358:
359: DefaultTreeModel model = (DefaultTreeModel) errorTree
360: .getModel();
361: DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath
362: .getLastPathComponent();
363:
364: if (o instanceof AbstractHint) {
365: AbstractHint hint = (AbstractHint) o;
366: boolean value = HintsSettings.isEnabled(hint,
367: getCurrentPrefernces(hint));
368: Preferences mn = getPreferences4Modification(hint);
369: HintsSettings.setEnabled(mn, !value);
370: model.nodeChanged(node);
371: model.nodeChanged(node.getParent());
372: } else if (o instanceof FileObject) {
373: boolean value = !isSelected(node);
374:
375: for (int i = 0; i < node.getChildCount(); i++) {
376: DefaultMutableTreeNode ch = (DefaultMutableTreeNode) node
377: .getChildAt(i);
378: Object cho = ch.getUserObject();
379: if (cho instanceof AbstractHint) {
380: AbstractHint hint = (AbstractHint) cho;
381: boolean cv = HintsSettings.isEnabled(hint,
382: getCurrentPrefernces(hint));
383: if (cv != value) {
384: Preferences mn = getPreferences4Modification(hint);
385: HintsSettings.setEnabled(mn, value);
386: model.nodeChanged(ch);
387: }
388: }
389: }
390: model.nodeChanged(node);
391: }
392:
393: return false;
394: }
395:
396: private void componentsSetEnabled(boolean enabled) {
397:
398: if (!enabled) {
399: customizerPanel.removeAll();
400: customizerPanel.getParent().invalidate();
401: ((JComponent) customizerPanel.getParent()).revalidate();
402: customizerPanel.getParent().repaint();
403: severityComboBox.setSelectedIndex(severity2index
404: .get(HintsSettings.SEVERITY_DEFAUT));
405: tasklistCheckBox
406: .setSelected(HintsSettings.IN_TASK_LIST_DEFAULT);
407: descriptionTextArea.setText(""); // NOI18N
408: }
409:
410: severityComboBox.setEnabled(enabled);
411: tasklistCheckBox.setEnabled(enabled);
412: descriptionTextArea.setEnabled(enabled);
413: }
414:
415: // Inner classes -----------------------------------------------------------
416:
417: private static class ModifiedPreferences extends
418: AbstractPreferences {
419:
420: private Map<String, Object> map = new HashMap<String, Object>();
421:
422: public ModifiedPreferences(Preferences node) {
423: super (null, ""); // NOI18N
424: try {
425: for (java.lang.String key : node.keys()) {
426: put(key, node.get(key, null));
427: }
428: } catch (BackingStoreException ex) {
429: Exceptions.printStackTrace(ex);
430: }
431: }
432:
433: public void store(Preferences target) {
434:
435: try {
436: for (String key : keys()) {
437: target.put(key, get(key, null));
438: }
439: } catch (BackingStoreException ex) {
440: Exceptions.printStackTrace(ex);
441: }
442:
443: }
444:
445: protected void putSpi(String key, String value) {
446: map.put(key, value);
447: }
448:
449: protected String getSpi(String key) {
450: return (String) map.get(key);
451: }
452:
453: protected void removeSpi(String key) {
454: map.remove(key);
455: }
456:
457: protected void removeNodeSpi() throws BackingStoreException {
458: throw new UnsupportedOperationException(
459: "Not supported yet.");
460: }
461:
462: protected String[] keysSpi() throws BackingStoreException {
463: String array[] = new String[map.keySet().size()];
464: return map.keySet().toArray(array);
465: }
466:
467: protected String[] childrenNamesSpi()
468: throws BackingStoreException {
469: throw new UnsupportedOperationException(
470: "Not supported yet.");
471: }
472:
473: protected AbstractPreferences childSpi(String name) {
474: throw new UnsupportedOperationException(
475: "Not supported yet.");
476: }
477:
478: protected void syncSpi() throws BackingStoreException {
479: throw new UnsupportedOperationException(
480: "Not supported yet.");
481: }
482:
483: protected void flushSpi() throws BackingStoreException {
484: throw new UnsupportedOperationException(
485: "Not supported yet.");
486: }
487:
488: }
489:
490: }
|