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.openide.explorer.propertysheet;
043:
044: import java.awt.BorderLayout;
045: import java.awt.BorderLayout;
046: import java.awt.Color;
047: import java.awt.Component;
048: import java.awt.Container;
049: import java.awt.Dimension;
050: import java.awt.FlowLayout;
051: import java.awt.Graphics;
052: import java.awt.KeyboardFocusManager;
053: import javax.swing.JFrame;
054: import java.util.StringTokenizer;
055: import java.awt.Point;
056: import java.awt.Rectangle;
057: import java.awt.Window;
058: import org.openide.explorer.propertysheet.PropertySheet;
059: import java.awt.event.ActionEvent;
060: import java.awt.event.ActionListener;
061: import java.awt.event.FocusEvent;
062: import java.awt.event.FocusListener;
063: import java.awt.event.KeyEvent;
064: import java.awt.event.MouseEvent;
065: import java.awt.event.WindowAdapter;
066: import java.awt.event.WindowEvent;
067: import java.awt.image.BufferedImage;
068: import org.openide.*;
069: import org.openide.nodes.*;
070: import org.openide.explorer.propertysheet.*;
071: import org.openide.explorer.propertysheet.editors.*;
072: import java.beans.*;
073: import java.beans.PropertyVetoException;
074: import java.io.File;
075: import java.lang.ref.WeakReference;
076: import java.lang.reflect.*;
077: import javax.swing.*;
078: import javax.swing.ImageIcon;
079: import javax.swing.JPanel;
080: import javax.swing.JTextField;
081: import javax.swing.event.ChangeEvent;
082: import javax.swing.event.ChangeListener;
083: import junit.framework.*;
084: import junit.textui.TestRunner;
085: import org.netbeans.junit.*;
086: import org.openide.ErrorManager;
087: import org.openide.util.Lookup;
088:
089: /* A comprehensive test of CustomEditorDisplayer */
090: public class CustomEditorDisplayerTest extends NbTestCase {
091:
092: static {
093: ComboTest.registerPropertyEditors();
094: }
095:
096: public CustomEditorDisplayerTest(String name) {
097: super (name);
098: }
099:
100: public static void main(String args[]) {
101: // LookAndFeel lf = UIManager.getLookAndFeel();
102: /* try {
103: UIManager.setLookAndFeel(new com.jgoodies.plaf.plastic.Plastic3DLookAndFeel());
104: } catch (Exception e) {
105: e.printStackTrace();
106: }
107: */
108:
109: TestRunner.run(suite());
110:
111: /*
112: boolean go=false;
113: try {
114: UIManager.setLookAndFeel(new PseudoWindowsLookAndFeel());
115: go = true;
116: } catch (NoClassDefFoundError e) {
117: System.err.println("Couldn't run tests on windows look and feel");
118: } catch (UnsupportedLookAndFeelException e) {
119: System.err.println("Couldn't run tests on windows look and feel");
120: }
121: if (go) {
122: TestRunner.run(suite ());
123: }
124: go=false;
125: try {
126: UIManager.setLookAndFeel(new com.sun.java.swing.plaf.gtk.GTKLookAndFeel());
127: go = true;
128: } catch (NoClassDefFoundError e) {
129: System.err.println("Couldn't run tests on GTK look and feel");
130: e.printStackTrace();
131: } catch (UnsupportedLookAndFeelException e) {
132: System.err.println("Couldn't run tests on GTK look and feel");
133: e.printStackTrace();
134: }
135: if (go) {
136: TestRunner.run(suite ());
137: }
138: try {
139: UIManager.setLookAndFeel(lf);
140: } catch (Exception e) {
141: //highly unlikely
142: }
143: */
144: try {
145: // new CustomEditorDisplayerTest("goo").setUp();
146: } catch (Exception e) {
147: }
148:
149: }
150:
151: static int idx = -1;
152:
153: public static Test suite() {
154: return new CustomEditorDisplayerSuite();
155: }
156:
157: private static class CustomEditorDisplayerSuite extends NbTestSuite {
158: public CustomEditorDisplayerSuite() {
159: super (CustomEditorDisplayerTest.class);
160: }
161:
162: public void run(final TestResult tr) {
163: super .run(tr);
164: }
165: }
166:
167: /*
168: * This test creates a Property, Editor and Node. First test checks if initialized
169: * editor contains the same value as property. The second checks if the property
170: * value is changed if the same change will be done in the editor.
171: */
172:
173: CustomEditorDisplayer basicRen;
174: CustomEditorDisplayer fileRen;
175:
176: private TNode tn;
177: private BasicProperty basicProp;
178: private FileProperty fileProp;
179: private BasicEditor te;
180:
181: private boolean setup = false;
182: private JFrame jf = null;
183: private JPanel jp = null;
184: private int SLEEP_LENGTH = 10;
185:
186: protected void tearDown() {
187: if (jf != null) {
188: jf.hide();
189: jf.dispose();
190: }
191: }
192:
193: protected void setUp() throws Exception {
194: // UIManager.setLookAndFeel(new com.sun.java.swing.plaf.windows.WindowsLookAndFeel());
195: // UIManager.setLookAndFeel(new com.sun.java.swing.plaf.gtk.GTKLookAndFeel());
196:
197: try {
198: if (setup)
199: return;
200: basicProp = new BasicProperty("basicProp", true);
201: fileProp = new FileProperty("FileProp", true);
202:
203: // Create new BasicEditor
204: te = new BasicEditor();
205: // Create new TNode
206: tn = new TNode();
207:
208: System.err.println("Crating frame");
209: jf = new JFrame();
210: jf.getContentPane().setLayout(new BorderLayout());
211: jp = new JPanel();
212: jp.setLayout(new FlowLayout());
213: jf.getContentPane().add(jp, BorderLayout.CENTER);
214: jf.setLocation(20, 20);
215: jf.setSize(600, 200);
216:
217: synchronized (jp.getTreeLock()) {
218: System.err.println("BasicProp = " + basicProp);
219:
220: basicRen = new CustomEditorDisplayer(basicProp);
221: fileRen = new CustomEditorDisplayer(fileProp);
222:
223: jp.add(basicRen.getComponent());
224:
225: jp.add(fileRen.getComponent());
226: }
227:
228: System.err.println("Waiting for window");
229: new WaitWindow(jf); //block until window open
230: System.err.println("Window shown");
231: } catch (Exception e) {
232: e.printStackTrace();
233: } finally {
234: setup = true;
235: }
236: }
237:
238: public void testEntryInCustomEditor() throws Exception {
239: //Just types into the value field and presses
240: basicRen
241: .setUpdatePolicy(PropertyDisplayer.UPDATE_ON_CONFIRMATION);
242:
243: BasicCustomEditor custom = (BasicCustomEditor) basicProp
244: .getPropertyEditor().getCustomEditor();
245:
246: clickOn(custom.valueField);
247: typeKey(custom.valueField, KeyEvent.VK_W);
248: typeKey(custom.valueField, KeyEvent.VK_O);
249: typeKey(custom.valueField, KeyEvent.VK_O);
250: typeKey(custom.valueField, KeyEvent.VK_G);
251: typeKey(custom.valueField, KeyEvent.VK_L);
252: typeKey(custom.valueField, KeyEvent.VK_E);
253:
254: Object pre = basicProp.getValue();
255:
256: pressKey(custom.valueField, KeyEvent.VK_ENTER);
257:
258: Object post = basicProp.getValue();
259:
260: assertTrue(
261: "After entering text in editor, value should be the text. Expected WOOGLE got "
262: + post, "WOOGLE".equals(post));
263:
264: assertNotSame(
265: "After entering data in the custom editor and pressing enter with policy UPDATE_ON_CONFIRMATION, property value should be changed",
266: pre, post);
267:
268: basicRen
269: .setUpdatePolicy(PropertyDisplayer.UPDATE_ON_EXPLICIT_REQUEST);
270:
271: clickOn(custom.valueField);
272: typeKey(custom.valueField, KeyEvent.VK_N);
273: typeKey(custom.valueField, KeyEvent.VK_I);
274: typeKey(custom.valueField, KeyEvent.VK_F);
275: typeKey(custom.valueField, KeyEvent.VK_T);
276: typeKey(custom.valueField, KeyEvent.VK_Y);
277:
278: pre = basicProp.getValue();
279:
280: pressKey(custom.valueField, KeyEvent.VK_ENTER);
281:
282: post = basicProp.getValue();
283:
284: assertTrue(
285: "After entering text in editor with policy UPDATE_ON_EXPLICIT_REQUEST, the property value should not be the entered text. Expected NIFTY got "
286: + post, "WOOGLE".equals(post));
287:
288: assertSame(
289: "After entering data in the custom editor and pressing enter with policy UPDATE_ON_EXPLICIT_REQUEST, property value should NOT be changed",
290: pre, post);
291:
292: }
293:
294: public void testFailureModes() throws Exception {
295: basicRen
296: .setUpdatePolicy(PropertyDisplayer.UPDATE_ON_CONFIRMATION);
297: BasicCustomEditor custom = (BasicCustomEditor) basicProp
298: .getPropertyEditor().getCustomEditor();
299:
300: custom.setInvalidValueButton.doClick();
301: sleep();
302: sleep();
303: requestFocus(custom.valueField);
304:
305: IllegalArgumentException iae = null;
306: try {
307: pressKey(custom.valueField, KeyEvent.VK_ENTER);
308: } catch (IllegalArgumentException e) {
309: iae = e;
310: }
311: assertNotNull("Entering a bad value should throw an exception",
312: iae);
313: iae = null;
314:
315: custom.setDontAllowValidateButton.doClick();
316: sleep();
317: sleep();
318: requestFocus(custom.valueField);
319: try {
320: pressKey(custom.valueField, KeyEvent.VK_ENTER);
321: } catch (IllegalArgumentException e) {
322: iae = e;
323: }
324: assertNotNull(
325: "If a state change on the PropertyEnv causes a PropertyVetoException, an illegal argument exception should be thrown with the message from the PVE",
326: iae);
327: iae = null;
328:
329: BasicEditor editor = (BasicEditor) basicRen.getPropertyEditor();
330:
331: PropertyEnv env = basicRen.getPropertyEnv();
332: assertEquals(
333: "After a failure to validate, the editor's property env's state should be STATE_INVALID",
334: PropertyEnv.STATE_INVALID, env.getState());
335:
336: assertSame(
337: "After a failure to validate, the PropertyEnv the editor is talking to should be the one owned by the CustomEditorDisplayer",
338: env, editor.env);
339: }
340:
341: public void testValidationMethods() throws Exception {
342: basicRen
343: .setUpdatePolicy(PropertyDisplayer.UPDATE_ON_EXPLICIT_REQUEST);
344: BasicCustomEditor custom = (BasicCustomEditor) basicProp
345: .getPropertyEditor().getCustomEditor();
346:
347: clickOn(custom.valueField);
348: typeKey(custom.valueField, KeyEvent.VK_F);
349: typeKey(custom.valueField, KeyEvent.VK_U);
350: typeKey(custom.valueField, KeyEvent.VK_N);
351: typeKey(custom.valueField, KeyEvent.VK_K);
352: typeKey(custom.valueField, KeyEvent.VK_Y);
353: pressKey(custom.valueField, KeyEvent.VK_ENTER);
354:
355: assertTrue(
356: "After entering text with update policy UPDATE_ON_EXPLICIT_REQUEST, isValueModified should return true",
357: basicRen.isValueModified());
358: String legality = basicRen.isModifiedValueLegal();
359: assertTrue(
360: "After entering a legal value with update policy UPDATE_ON_EXPLICIT_REQUEST, isModifiedValueLegal should return null but returned "
361: + legality, legality == null);
362:
363: Exception e = null;
364: try {
365: basicRen.commit();
366: } catch (Exception e1) {
367: e = e1;
368: }
369: assertNull(
370: "Committing a legal value should not throw an exception",
371: e);
372: assertEquals(
373: "Calling commit() with update policy UPDATE_ON_EXPLICIT_REQUEST should store the edited value in the property",
374: "FUNKY", basicProp.getValue());
375:
376: assertTrue(
377: "After committing a legal value, isValueModified should return false",
378: !basicRen.isValueModified());
379: assertNull(
380: "After committing a legal value, isModifiedValueLegal should return null",
381: basicRen.isModifiedValueLegal());
382:
383: custom.setDontAllowValidateButton.doClick();
384: sleep();
385: sleep();
386:
387: try {
388: pressKey(custom.valueField, KeyEvent.VK_ENTER);
389: } catch (Exception e2) {
390:
391: }
392:
393: assertNotNull(
394: "With an unvalidatable value, isModifiedValueLegal should return a localized message",
395: basicRen.isModifiedValueLegal());
396: assertTrue(
397: "With an unvalidatable value, isValueModified should return true",
398: basicRen.isValueModified());
399:
400: custom.valueField.setText("foo goo");
401: custom.setDontAllowValidateButton.doClick();
402: sleep();
403: sleep();
404:
405: try {
406: basicRen.commit();
407: } catch (Exception e3) {
408: e = e3;
409: }
410:
411: assertNotNull(
412: "Committing an unvalidatable value should throw an exception",
413: e);
414:
415: }
416:
417: private class FL implements FocusListener {
418: private FocusEvent gainedEvent = null;
419: private FocusEvent lostEvent = null;
420: private int gainedCount = 0;
421: private int lostCount = 0;
422:
423: public void assertGained() {
424: assertNotNull(
425: "No focus gained received after clicking on an editable renderer",
426: gainedEvent);
427: assertTrue(
428: "Received wrong number of focus gained events for a single click on a renderer "
429: + gainedCount, gainedCount == 1);
430: }
431:
432: public void assertLost() {
433: assertNotNull(
434: "No focus lost event received after clicking away from a focused, editable renderer",
435: lostEvent);
436: assertTrue(
437: "Received wrong number of focus lost events for a single click away from a focused renderer"
438: + lostCount, lostCount == 1);
439: }
440:
441: public void focusGained(java.awt.event.FocusEvent e) {
442: gainedEvent = e;
443: gainedCount++;
444: }
445:
446: public void focusLost(java.awt.event.FocusEvent e) {
447: lostEvent = e;
448: lostCount++;
449: }
450: }
451:
452: private class CL implements ChangeListener {
453:
454: private ChangeEvent e;
455:
456: public void assertEvent(String msg) {
457: sleep(); //give the event time to happen
458: assertNotNull(msg, e);
459: e = null;
460: }
461:
462: public void assertNoEvent(String msg) {
463: sleep();
464: assertNull(e);
465: e = null;
466: }
467:
468: public void stateChanged(ChangeEvent e) {
469: this .e = e;
470: }
471:
472: }
473:
474: private static class TestGCVal extends Object {
475: public String toString() {
476: return "TestGCVal";
477: }
478: }
479:
480: private static class WaitWindow extends WindowAdapter {
481: boolean shown = false;
482:
483: public WaitWindow(JFrame f) {
484: f.addWindowListener(this );
485: f.show();
486: if (!shown) {
487: synchronized (this ) {
488: try {
489: //System.err.println("Waiting for window");
490: wait(5000);
491: } catch (Exception e) {
492: }
493: }
494: }
495: }
496:
497: public void windowOpened(WindowEvent e) {
498: shown = true;
499: synchronized (this ) {
500: //System.err.println("window opened");
501: notifyAll();
502: ((JFrame) e.getSource()).removeWindowListener(this );
503: }
504: }
505: }
506:
507: private void sleep() {
508: //useful when running interactively
509:
510: try {
511: Thread.currentThread().sleep(SLEEP_LENGTH);
512: } catch (InterruptedException ie) {
513: //go away
514: }
515:
516: //runs faster -uncomment for production use
517:
518: try {
519: //jf.getTreeLock().wait();
520: SwingUtilities.invokeAndWait(new Runnable() {
521: public void run() {
522: System.currentTimeMillis();
523: }
524: });
525: //jf.getTreeLock().wait();
526: SwingUtilities.invokeAndWait(new Runnable() {
527: public void run() {
528: System.currentTimeMillis();
529: }
530: });
531: } catch (Exception e) {
532: }
533:
534: }
535:
536: private void requestFocus(final JComponent jc) throws Exception {
537: SwingUtilities.invokeAndWait(new Runnable() {
538: public void run() {
539: jc.requestFocus();
540: }
541: });
542: sleep();
543: }
544:
545: private void changeProperty(final RendererPropertyDisplayer ren,
546: final Node.Property newProp) throws Exception {
547: SwingUtilities.invokeAndWait(new Runnable() {
548: public void run() {
549: ren.setProperty(newProp);
550: }
551: });
552: }
553:
554: private void clickOn(final JComponent ren, final int fromRight,
555: final int fromTop) throws Exception {
556: SwingUtilities.invokeAndWait(new Runnable() {
557: public void run() {
558: Point toClick = new Point(ren.getWidth() - fromRight,
559: fromTop);
560: Component target = ren.getComponentAt(toClick);
561: toClick = SwingUtilities.convertPoint(ren, toClick,
562: target);
563: System.err.println("Target component is "
564: + target.getClass().getName() + " - " + target
565: + " clicking at " + toClick);
566:
567: MouseEvent me = new MouseEvent(target,
568: MouseEvent.MOUSE_PRESSED, System
569: .currentTimeMillis(),
570: MouseEvent.BUTTON1_MASK, toClick.x, toClick.y,
571: 2, false);
572: target.dispatchEvent(me);
573: me = new MouseEvent(target, MouseEvent.MOUSE_RELEASED,
574: System.currentTimeMillis(),
575: MouseEvent.BUTTON1_MASK, toClick.x, toClick.y,
576: 2, false);
577: target.dispatchEvent(me);
578: me = new MouseEvent(target, MouseEvent.MOUSE_CLICKED,
579: System.currentTimeMillis(),
580: MouseEvent.BUTTON1_MASK, toClick.x, toClick.y,
581: 2, false);
582: }
583: });
584: sleep();
585: }
586:
587: private void clickOn(final JComponent ren) throws Exception {
588: SwingUtilities.invokeAndWait(new Runnable() {
589: public void run() {
590: Point toClick = new Point(5, 5);
591: Component target = ren.getComponentAt(toClick);
592: MouseEvent me = new MouseEvent(target,
593: MouseEvent.MOUSE_PRESSED, System
594: .currentTimeMillis(),
595: MouseEvent.BUTTON1_MASK, toClick.x, toClick.y,
596: 2, false);
597: target.dispatchEvent(me);
598: }
599: });
600: sleep();
601: }
602:
603: private void setEnabled(final CustomEditorDisplayer ren,
604: final boolean val) throws Exception {
605: SwingUtilities.invokeAndWait(new Runnable() {
606: public void run() {
607: ren.setEnabled(val);
608: }
609: });
610: sleep();
611: }
612:
613: private Exception throwMe = null;
614: private String flushResult = null;
615:
616: private String flushValue(final CustomEditorDisplayer ren)
617: throws Exception {
618: SwingUtilities.invokeAndWait(new Runnable() {
619: public void run() {
620: try {
621: //flushResult = ren.flushValue();
622: } catch (Exception e) {
623: throwMe = e;
624: flushResult = null;
625: }
626: }
627: });
628: if (throwMe != null) {
629: try {
630: throw throwMe;
631: } finally {
632: throwMe = null;
633: }
634: }
635: return flushResult;
636: }
637:
638: private void releaseKey(final Component target, final int key)
639: throws Exception {
640: SwingUtilities.invokeAndWait(new Runnable() {
641: public void run() {
642: KeyEvent ke = new KeyEvent(target,
643: KeyEvent.KEY_RELEASED, System
644: .currentTimeMillis(), 0, key,
645: (char) key);
646: target.dispatchEvent(ke);
647: }
648: });
649: sleep();
650: }
651:
652: private Exception throwMe2 = null;
653:
654: private void pressKey(final Component target, final int key)
655: throws Exception {
656: SwingUtilities.invokeAndWait(new Runnable() {
657: public void run() {
658: KeyEvent ke = new KeyEvent(target,
659: KeyEvent.KEY_PRESSED, System
660: .currentTimeMillis(), 0, key,
661: (char) key);
662: try {
663: target.dispatchEvent(ke);
664: } catch (Exception e) {
665: throwMe2 = e;
666: }
667: }
668: });
669: sleep();
670: if (throwMe2 != null) {
671: Exception e1 = throwMe2;
672: throwMe2 = null;
673: throw e1;
674: }
675: }
676:
677: private void shiftPressKey(final Component target, final int key)
678: throws Exception {
679: SwingUtilities.invokeAndWait(new Runnable() {
680: public void run() {
681: KeyEvent ke = new KeyEvent(target,
682: KeyEvent.KEY_PRESSED, System
683: .currentTimeMillis(),
684: KeyEvent.SHIFT_MASK, key, (char) key);
685: target.dispatchEvent(ke);
686: }
687: });
688: sleep();
689: }
690:
691: private void typeKey(final Component target, final int key)
692: throws Exception {
693: SwingUtilities.invokeAndWait(new Runnable() {
694: public void run() {
695: KeyEvent ke = new KeyEvent(target, KeyEvent.KEY_TYPED,
696: System.currentTimeMillis(), 0,
697: KeyEvent.VK_UNDEFINED, (char) key);
698: target.dispatchEvent(ke);
699: }
700: });
701: sleep();
702: }
703:
704: //Node definition
705: public class TNode extends AbstractNode {
706: //create Node
707: public TNode() {
708: super (Children.LEAF);
709: setName("TNode"); // or, super.setName if needed
710: setDisplayName("TNode");
711: createSheet();
712: }
713:
714: //clone existing Node
715: public Node cloneNode() {
716: return new TNode();
717: }
718:
719: public void addProp(Node.Property p) {
720: props.put(p);
721: this .firePropertyChange(PROP_PROPERTY_SETS, null, null);
722: this .firePropertySetsChange(null, null);
723: }
724:
725: Sheet sheet = null;
726: Sheet.Set props = null;
727:
728: // Create a property sheet:
729: protected Sheet createSheet() {
730: sheet = super .createSheet();
731: // Make sure there is a "Properties" set:
732: props = sheet.get(Sheet.PROPERTIES);
733: if (props == null) {
734: props = Sheet.createPropertiesSet();
735: sheet.put(props);
736: }
737: props.put(basicProp);
738: props.put(fileProp);
739:
740: return sheet;
741: }
742:
743: // Method firing changes
744: public void fireMethod(String s, Object o1, Object o2) {
745: firePropertyChange(s, o1, o2);
746: }
747: }
748:
749: // Property definition
750: public class BasicProperty extends PropertySupport {
751: private Object myValue = "Value";
752:
753: // Create new Property
754: public BasicProperty(String name, boolean isWriteable) {
755: super (name, Object.class, name, "", true, isWriteable);
756: }
757:
758: // get property value
759: public Object getValue() {
760: return myValue;
761: }
762:
763: // set property value
764: public void setValue(Object value)
765: throws IllegalArgumentException,
766: IllegalAccessException, InvocationTargetException {
767: System.err.println("BASICPROP setValue to " + value
768: + " (was " + myValue + ")");
769: Object oldVal = myValue;
770: myValue = value;
771: tn.fireMethod(getName(), oldVal, myValue);
772: }
773:
774: // get the property editor
775: public PropertyEditor getPropertyEditor() {
776: return te;
777: }
778: }
779:
780: // Property definition
781: public class FileProperty extends PropertySupport {
782: private Object myValue = new File("aFile");
783:
784: // Create new Property
785: public FileProperty(String name, boolean isWriteable) {
786: super (name, File.class, name, "", true, isWriteable);
787: }
788:
789: // get property value
790: public Object getValue() {
791: return myValue;
792: }
793:
794: // set property value
795: public void setValue(Object value)
796: throws IllegalArgumentException,
797: IllegalAccessException, InvocationTargetException {
798: Object oldVal = myValue;
799: myValue = value;
800: tn.fireMethod(getName(), oldVal, myValue);
801: }
802: }
803:
804: // Editor definition
805: public class BasicEditor extends PropertyEditorSupport implements
806: ExPropertyEditor, PropertyChangeListener,
807: VetoableChangeListener {
808: PropertyEnv env;
809:
810: // Create new BasicEditor
811: public BasicEditor() {
812: }
813:
814: /*
815: * This method is called by the IDE to pass
816: * the environment to the property editor.
817: */
818: public void attachEnv(PropertyEnv env) {
819: if (env != null) {
820: env.removeVetoableChangeListener(this );
821: }
822: this .env = env;
823:
824: env.setState(env.STATE_VALID);
825: env.addVetoableChangeListener(this );
826: System.err.println(" ATTACHENV");
827:
828: }
829:
830: // Set that this Editor doesn't support custom Editor
831: public boolean supportsCustomEditor() {
832: return true;
833: }
834:
835: // Set the Property value threw the Editor
836: public void setValue(Object newValue) {
837: System.err.println(" BasicEditor.setValue: " + newValue);
838: super .setValue(newValue);
839: }
840:
841: public String getAsText() {
842: return getValue() == null ? "null" : getValue().toString();
843: }
844:
845: private Component custom;
846:
847: public Component getCustomEditor() {
848: if (custom == null) {
849: custom = new BasicCustomEditor(this );
850: }
851: return custom;
852: }
853:
854: public void vetoNext() {
855: env.setState(env.STATE_NEEDS_VALIDATION);
856: vetoNextChange = true;
857: System.err.println(" veto next");
858: }
859:
860: public void propertyChange(PropertyChangeEvent evt) {
861:
862: }
863:
864: boolean vetoNextChange = false;
865:
866: public void vetoableChange(PropertyChangeEvent e)
867: throws PropertyVetoException {
868: System.err.println("GOT A VETOABLE CHANGE IN BASIC EDITOR");
869: PropertyEnv env = (PropertyEnv) e.getSource();
870: if ((vetoNextChange || "Dont allow validate"
871: .equals(getAsText()))
872: && PropertyEnv.STATE_NEEDS_VALIDATION.equals(env
873: .getState())) {
874: System.err.println(" VETOING");
875: PropertyVetoException pve = new PropertyVetoException(
876: "NoNoNoNoNo", e);
877: ErrorManager.getDefault().annotate(pve,
878: ErrorManager.USER, null, "You can't do that!",
879: null, null);
880: vetoNextChange = false;
881: throw pve;
882: }
883: }
884:
885: public void setAsText(String s) {
886: System.err.println(" BasicEditor.setAsText: " + s);
887: if ("invalidValue".equals(s)) {
888: IllegalArgumentException iae = new IllegalArgumentException();
889: ErrorManager.getDefault().annotate(iae,
890: ErrorManager.USER, "invalid value", "No way",
891: null, null);
892: throw iae;
893: }
894: setValue(s);
895: }
896:
897: }
898:
899: public class BasicCustomEditor extends JPanel implements
900: ActionListener {
901: JTextField valueField = new JTextField();
902: JButton setInvalidValueButton = new JButton("Invalid value");
903: JButton setDontAllowValidateButton = new JButton(
904: "Dont allow validate");
905: BasicEditor editor;
906:
907: public BasicCustomEditor(BasicEditor editor) {
908: this .editor = editor;
909: init();
910: }
911:
912: private void init() {
913: setLayout(new FlowLayout());
914: valueField.addActionListener(this );
915: setInvalidValueButton.addActionListener(this );
916: setDontAllowValidateButton.addActionListener(this );
917: valueField.setColumns(30);
918: setBackground(Color.ORANGE);
919: add(valueField);
920: add(setInvalidValueButton);
921: add(setDontAllowValidateButton);
922: }
923:
924: boolean processing;
925:
926: public void actionPerformed(ActionEvent e) {
927: processing = true;
928: try {
929: if (e.getSource() == setDontAllowValidateButton) {
930: editor.vetoNext();
931: valueField.setText("dont allow validate");
932: }
933: if (e.getSource() == setInvalidValueButton) {
934: valueField.setText("invalidValue");
935: }
936: if (e.getSource() == valueField) {
937: editor.setAsText(valueField.getText());
938: }
939: editor.env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
940: } finally {
941: processing = false;
942: }
943: }
944:
945: }
946: }
|