KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > multiview > ui > SectionInnerPanel


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.multiview.ui;
21
22 import java.awt.event.ActionEvent JavaDoc;
23 import java.awt.event.ActionListener JavaDoc;
24 import org.netbeans.modules.xml.multiview.Error;
25 import org.netbeans.modules.xml.multiview.Refreshable;
26 import org.netbeans.modules.xml.multiview.Utils;
27 import org.netbeans.modules.xml.multiview.cookies.ErrorLocator;
28 import org.netbeans.modules.xml.multiview.cookies.LinkCookie;
29 import org.openide.util.RequestProcessor;
30
31 import javax.swing.*;
32 import javax.swing.text.JTextComponent JavaDoc;
33 import java.awt.*;
34 import java.awt.event.FocusEvent JavaDoc;
35 import java.awt.event.FocusListener JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.LinkedList JavaDoc;
38
39 /**
40  * This class represents a panel that is contained in <code>SectionPanel</code>,
41  * i.e. an inner panel as the name implies. This class provides support for registering
42  * UI components that modify the model by means of various <code>addModifier</code> and
43  * <code>addImmediateModifier</code> methods. See javadocs of those for
44  * more details.
45  *
46  * @author mkuchtiak
47  */

48 public abstract class SectionInnerPanel extends javax.swing.JPanel JavaDoc implements LinkCookie, ErrorLocator {
49     private SectionView sectionView;
50     private java.util.List JavaDoc refreshableList = new LinkedList JavaDoc();
51     
52     private boolean localFocusListenerInitialized = false;
53     private FocusListener JavaDoc localFocusListener = new FocusListener JavaDoc() {
54         public void focusGained(FocusEvent JavaDoc evt) {
55             final FocusListener JavaDoc[] focusListeners = getFocusListeners();
56             for (int i = 0; i < focusListeners.length; i++) {
57                 focusListeners[i].focusGained(evt);
58             }
59         }
60         
61         public void focusLost(FocusEvent JavaDoc evt) {
62             processFocusEvent(evt);
63         }
64     };
65     
66     private RequestProcessor.Task refreshTask = RequestProcessor.getDefault().create(new Runnable JavaDoc() {
67         public void run() {
68             refreshView();
69         }
70     });
71     
72     private static final int REFRESH_DELAY = 50;
73     private FlushFocusListener activeListener = null;
74     private boolean closing = false;
75     
76     /** Constructor that takes the enclosing SectionView object as its argument
77      * @param sectionView enclosing SectionView object
78      */

79     public SectionInnerPanel(SectionView sectionView) {
80         this.sectionView = sectionView;
81     }
82     
83     public synchronized void addFocusListener(FocusListener JavaDoc l) {
84         super.addFocusListener(l);
85         if (!localFocusListenerInitialized) {
86             localFocusListenerInitialized = true;
87             Container container = this;
88             FocusListener JavaDoc focusListener = localFocusListener;
89             addFocusListenerRecursively(container, focusListener);
90         }
91     }
92     
93     private void addFocusListenerRecursively(Container container, FocusListener JavaDoc focusListener) {
94         final Component JavaDoc[] components = container.getComponents();
95         for (int i = 0; i < components.length; i++) {
96             Component JavaDoc component = components[i];
97             if (component.isFocusable() && !(component instanceof JLabel)) {
98                 component.addFocusListener(focusListener);
99             }
100             if (component instanceof Container) {
101                 if (!(component instanceof SectionNodePanel)) {
102                     addFocusListenerRecursively((Container) component, focusListener);
103                 }
104             }
105         }
106     }
107     
108     /** Getter for section view
109      * @return sectionView enclosing SectionView object
110      */

111     public SectionView getSectionView() {
112         return sectionView;
113     }
114     
115     /**
116      * Callback method called on focus lost event after the value was checked for correctness
117      * (in case <code>source</code> was registered using one of the <code>addModifier</code> methods
118      * of this class, if <code>addImmediateModifier</code> was used instead, this method
119      * gets called immediately when the state of <code>source</code> changes).
120      * @param source the last focused JComponent or whose state was changed.
121      * @param value the value that has been set (typed) in the component
122      */

123     public abstract void setValue(JComponent source, Object JavaDoc value);
124     
125     /** Callback method called on document change event. This is called for components that
126      * require just-in-time validation.
127      * @param source JTextComponent being actually edited
128      * @param value the actual value of the component
129      */

130     public void documentChanged(javax.swing.text.JTextComponent JavaDoc source, String JavaDoc value) {
131     }
132     
133     /** Callback method called on focus lost event after the value was checked for correctness.
134      * and the result is that the value is wrong. The value should be rollbacked from the model.
135      * @param source last focused JComponent
136      */

137     public void rollbackValue(javax.swing.text.JTextComponent JavaDoc source) {
138     }
139     
140     /** Adds text component to the set of JTextComponents that modifies the model.
141      * After the value in this component is changed the setValue() method is called.
142      * @param tc JTextComponent whose content is related to data model
143      */

144     public final void addModifier(final JTextComponent JavaDoc tc) {
145         tc.addFocusListener(new ModifyFocusListener(tc));
146     }
147     
148     /** Adds text component to the set of JTextComponents that modifies the model.
149      * After the value in this component is changed the setValue() method is called.
150      *
151      * @param tc JTextComponent whose content is related to data model
152      * @param test indicates whether the original value should be tested against the current value when focus is lost
153      */

154     public final void addModifier(final JTextComponent JavaDoc tc, boolean test) {
155         tc.addFocusListener(new ModifyFocusListener(tc, test));
156     }
157     
158     /**
159      * Adds combo box component to the set of JComboBoxes that modifies the model.
160      * After the value in this component is changed the setValue() method is called.
161      * @param comboBox JComboBox whose content is related to data model
162      */

163     public final void addModifier(final JComboBox comboBox) {
164         comboBox.addFocusListener(new ComboBoxModifyFocusListener(comboBox));
165     }
166     
167     /**
168      * Adds combo box component to the set of JComboBoxes that modifies the model.
169      * After the value in this component is changed the setValue() method is called.
170      *
171      * @param comboBox JComboBox whose content is related to data model
172      * @param test indicates whether the original value should be tested against the current value when focus is lost
173      */

174     public final void addModifier(final JComboBox comboBox, boolean test) {
175         comboBox.addFocusListener(new ComboBoxModifyFocusListener(comboBox, test));
176     }
177     
178     
179     /**
180      * Adds radio button component to the set of JRadioButtons that modifies the model.
181      * After the value in this component is changed the setValue() method is called.
182      * @param radioButton JRadioButton whose content is related to data model
183      */

184     public final void addModifier(final JRadioButton radioButton){
185         radioButton.addFocusListener(new RadioButtonModifyFocusListener(radioButton));
186     }
187     
188     /**
189      * Adds check box component to the set of JCheckBox that modifies the model.
190      * After the value in this component is changed the setValue() method is called.
191      * @param checkBox JCheckBox whose content is related to data model
192      */

193     public final void addModifier(final JCheckBox checkBox){
194         checkBox.addFocusListener(new CheckBoxModifyFocusListener(checkBox));
195     }
196     
197     /**
198      * Adds check box component to the set of JCheckBoxes that modifies the model.
199      * After the value in this component is changed the setValue() method is called.
200      * Unlike <code>addModifier</code>, this method will cause <code>setValue()</code>
201      * to be called on each action event related to the given <code>checkBox</code>. Note
202      * that the same component should not be added both as a regular modifier and
203      * as an immediate modifier.
204      * @param checkBox JCheckBox whose content is related to data model
205      */

206     public final void addImmediateModifier(final JCheckBox checkBox){
207         checkBox.addActionListener(new CheckBoxActionListener(checkBox));
208     }
209     
210     /**
211      * Adds the given <code>radioButton</code> to the set of JRadioButtons that modify the model.
212      * After the value in this component is changed the setValue() method is called.
213      * Unlike <code>addModifier</code>, this method will cause <code>setValue()</code>
214      * to be called on each action event related to the given <code>checkBox</code>. Note
215      * that the same component should not be added both as a regular modifier and
216      * as an immediate modifier.
217      * @param radioButton the radio button whose content is related to the data model
218      */

219     public final void addImmediateModifier(final JRadioButton radioButton){
220         radioButton.addActionListener(new RadioButtonActionListener(radioButton));
221     }
222
223     /**
224      * Adds combo box component to the set of JComboBoxes that modifies the model.
225      * After the value in this component is changed the setValue() method is called.
226      * Unlike <code>addModifier</code>, this method will cause <code>setValue()</code>
227      * to be called on each action event related to the given <code>comboBox</code>.Note
228      * that the same component should not be added both as a regular modifier and
229      * as an immediate modifier.
230      * @param comboBox JComboBox whose content is related to data model
231      */

232     public final void addImmediateModifier(final JComboBox comboBox) {
233         comboBox.addActionListener(new ComboBoxActionListener(comboBox));
234     }
235     
236     /** Adds text component to the set of JTextComponents that modifies the model.
237      * After the value in this component is changed the setValue() method is called.
238      * Unlike <code>addModifier</code>, this method will cause <code>setValue()</code>
239      * to be called on every change related to the given <code>tc</code>.Note
240      * that the same component should not be added both as a regular modifier and
241      * as an immediate modifier.
242      * @param tc JTextComponent whose content is related to data model
243      */

244     public final void addImmediateModifier(final JTextComponent JavaDoc tc) {
245         tc.getDocument().addDocumentListener(new TextListener(tc, true));
246     }
247     
248     /** Adds text component to the set of JTextComponents that should be validated for correctness.
249      * After the value in this component is changed either setValue() method is called(value is correct)
250      * or rollbackValue() method is called(value is incorrect). Also the documentChanged() method is called during editing.
251      * @param tc JTextComponent whose content is related to data model and should be validated before saving to data model.
252      */

253     public final void addValidatee(final JTextComponent JavaDoc tc) {
254         tc.getDocument().addDocumentListener(new TextListener(tc));
255         tc.addFocusListener(new ValidateFocusListener(tc));
256     }
257     
258     protected void scheduleRefreshView() {
259         refreshTask.schedule(REFRESH_DELAY);
260     }
261     
262     /**
263      * Reloads data from data model
264      */

265     public void refreshView() {
266         for (Iterator JavaDoc it = refreshableList.iterator(); it.hasNext();) {
267             ((Refreshable) it.next()).refresh();
268         }
269     }
270     
271     protected void addRefreshable(Refreshable refreshable) {
272         refreshableList.add(refreshable);
273     }
274     
275     public void dataModelPropertyChange(Object JavaDoc source, String JavaDoc propertyName, Object JavaDoc oldValue, Object JavaDoc newValue) {
276         scheduleRefreshView();
277     }
278     
279     private class TextListener implements javax.swing.event.DocumentListener JavaDoc {
280         
281         private JTextComponent JavaDoc tc;
282         private boolean setValue = false;
283         
284         TextListener(JTextComponent JavaDoc tc) {
285             this(tc, false);
286         }
287         
288         TextListener(JTextComponent JavaDoc tc, boolean setValue) {
289             this.tc = tc;
290             this.setValue = setValue;
291         }
292         
293         /**
294          * Method from DocumentListener
295          */

296         public void changedUpdate(javax.swing.event.DocumentEvent JavaDoc evt) {
297             update(evt);
298         }
299         
300         /**
301          * Method from DocumentListener
302          */

303         public void insertUpdate(javax.swing.event.DocumentEvent JavaDoc evt) {
304             update(evt);
305         }
306         
307         /**
308          * Method from DocumentListener
309          */

310         public void removeUpdate(javax.swing.event.DocumentEvent JavaDoc evt) {
311             update(evt);
312         }
313         
314         private void update(javax.swing.event.DocumentEvent JavaDoc evt) {
315             if (setValue){
316                 startUIChange();
317                 setValue(tc, tc.getText());
318                 signalUIChange();
319                 endUIChange();
320             } else {
321                 documentChanged(tc, tc.getText());
322             }
323         }
324     }
325     
326     
327     private abstract class FlushFocusListener extends java.awt.event.FocusAdapter JavaDoc {
328         public abstract boolean flushData();
329     }
330     
331     private class ValidateFocusListener extends FlushFocusListener {
332         private String JavaDoc orgValue;
333         private boolean viewIsBuggy;
334         private final JTextComponent JavaDoc tc;
335         /**
336          * Prevents 'fix now' dialog from popping up multiple times.
337          */

338         private boolean disable;
339         
340         public ValidateFocusListener(JTextComponent JavaDoc tc) {
341             this.tc = tc;
342         }
343         
344         public void focusGained(FocusEvent JavaDoc evt) {
345             activeListener = this;
346             orgValue = tc.getText();
347             if (sectionView.getErrorPanel().getError() != null) {
348                 viewIsBuggy = true;
349             } else {
350                 viewIsBuggy = false;
351             }
352         }
353         
354         public void focusLost(FocusEvent JavaDoc evt) {
355             if (!closing) {
356                 if (!flushData()) {
357                     Utils.runInAwtDispatchThread(new Runnable JavaDoc() {
358                         public void run() {
359                             //todo: make sure the panel is visible
360
tc.requestFocus();
361                         }
362                     });
363                 } else {
364                     disable = false;
365                     activeListener = null;
366                 }
367             }
368         }
369         
370         public boolean flushData() {
371             Error JavaDoc error = sectionView.getErrorPanel().getError();
372             if (error != null && error.isEditError() && tc == error.getFocusableComponent()) {
373                 if (Error.TYPE_WARNING == error.getSeverityLevel() && !disable) {
374                     org.openide.DialogDescriptor desc = new RefreshSaveDialog(sectionView.getErrorPanel());
375                     Dialog dialog = org.openide.DialogDisplayer.getDefault().createDialog(desc);
376                     dialog.show();
377                     Integer JavaDoc opt = (Integer JavaDoc) desc.getValue();
378                     if (opt.equals(RefreshSaveDialog.OPTION_FIX)) {
379                         disable = true;
380                         return false;
381                     } else if (opt.equals(RefreshSaveDialog.OPTION_REFRESH)) {
382                         rollbackValue(tc);
383                         sectionView.checkValidity();
384                     } else {
385                         startUIChange();
386                         setValue(tc, tc.getText());
387                         signalUIChange();
388                         endUIChange();
389                         sectionView.checkValidity();
390                     }
391                 } else if (!disable){
392                     org.openide.DialogDescriptor desc = new RefreshDialog(sectionView.getErrorPanel());
393                     Dialog dialog = org.openide.DialogDisplayer.getDefault().createDialog(desc);
394                     dialog.show();
395                     Integer JavaDoc opt = (Integer JavaDoc) desc.getValue();
396                     if (opt.equals(RefreshDialog.OPTION_FIX)) {
397                         disable = true;
398                         return false;
399                     } else if (opt.equals(RefreshDialog.OPTION_REFRESH)) {
400                         rollbackValue(tc);
401                         sectionView.checkValidity();
402                     }
403                 }
404             } else {
405                 if (!tc.getText().equals(orgValue)) {
406                     startUIChange();
407                     setValue(tc, tc.getText());
408                     signalUIChange();
409                     endUIChange();
410                     sectionView.checkValidity();
411                 } else {
412                     if (viewIsBuggy) {
413                         sectionView.checkValidity();
414                     }
415                 }
416             }
417             disable = false;
418             return true;
419         }
420     }
421     
422     private class ModifyFocusListener extends FlushFocusListener {
423         private String JavaDoc orgValue;
424         private final JTextComponent JavaDoc tc;
425         // indicates whether the original value (orgValue) should be tested against the current value when focus is lost
426
private boolean test;
427         
428         public ModifyFocusListener(JTextComponent JavaDoc tc) {
429             this(tc, true);
430         }
431         
432         public ModifyFocusListener(JTextComponent JavaDoc tc, boolean test) {
433             this.tc = tc;
434             this.test = test;
435         }
436         
437         public void focusGained(FocusEvent JavaDoc evt) {
438             orgValue = tc.getText();
439             activeListener = this;
440         }
441         
442         public void focusLost(FocusEvent JavaDoc evt) {
443             if (!closing) {
444                 flushData();
445                 activeListener = null;
446             }
447         }
448         
449         public boolean flushData() {
450             if (!test || !tc.getText().equals(orgValue)) {
451                 startUIChange();
452                 setValue(tc, tc.getText());
453                 signalUIChange();
454                 endUIChange();
455             }
456             return true;
457         }
458     }
459     
460     /**
461      * Listener attached to combo boxes that modify the model.
462      */

463     private class ComboBoxModifyFocusListener extends FlushFocusListener {
464         private Object JavaDoc orgValue;
465         private final JComboBox comboBox;
466         // indicates whether the original value (orgValue) should be tested against the current value when focus is lost
467
private boolean test;
468         
469         public ComboBoxModifyFocusListener(JComboBox comboBox) {
470             this(comboBox, true);
471         }
472         
473         public ComboBoxModifyFocusListener(JComboBox comboBox, boolean test) {
474             this.comboBox = comboBox;
475             this.test = test;
476         }
477         
478         public void focusGained(FocusEvent JavaDoc evt) {
479             orgValue = comboBox.getSelectedItem();
480             activeListener = this;
481         }
482         
483         public void focusLost(FocusEvent JavaDoc evt) {
484             if (!closing) {
485                 flushData();
486                 activeListener = null;
487             }
488         }
489         
490         public boolean flushData() {
491             Object JavaDoc newValue = comboBox.getSelectedItem();
492             boolean newEqualsOld = (newValue==null ? orgValue==null : newValue.equals(orgValue));
493             if (!test || !newEqualsOld) {
494                 startUIChange();
495                 setValue(comboBox, comboBox.getSelectedItem());
496                 signalUIChange();
497                 endUIChange();
498             }
499             return true;
500         }
501     }
502     
503     /**
504      * Listener attached to radio buttons that modify the model.
505      */

506     private class RadioButtonModifyFocusListener extends FlushFocusListener {
507         private boolean orgValue;
508         private final JRadioButton radioButton;
509         
510         public RadioButtonModifyFocusListener(JRadioButton radioButton) {
511             this.radioButton = radioButton;
512         }
513         
514         public void focusGained(FocusEvent JavaDoc evt) {
515             orgValue = radioButton.isSelected();
516             activeListener = this;
517         }
518         
519         public void focusLost(FocusEvent JavaDoc evt) {
520             if (!closing) {
521                 flushData();
522                 activeListener = null;
523             }
524         }
525         
526         public boolean flushData() {
527             if (!(radioButton.isSelected() == orgValue)) {
528                 startUIChange();
529                 setValue(radioButton, Boolean.valueOf(radioButton.isSelected()));
530                 signalUIChange();
531                 endUIChange();
532             }
533             return true;
534         }
535     }
536     
537     public boolean canClose() {
538         closing = true;
539         try {
540             if (activeListener != null) {
541                 return activeListener.flushData();
542             }
543             return true;
544         } finally {
545             closing = false;
546         }
547     }
548     
549     /**
550      * Base class for action listeners for components that require setValue to be called
551      * immediately on action event (instead of when focus is gained/lost).
552      */

553     private abstract class FlushActionListener implements ActionListener JavaDoc {
554         
555         public final void actionPerformed(ActionEvent JavaDoc e) {
556             startUIChange();
557             doSetValue(e);
558             signalUIChange();
559             endUIChange();
560         }
561         
562         /**
563          * Does the actual setting of the value to the model. Subclasses
564          * should usually invoke <code>setValue</code> method here.
565          */

566         public abstract void doSetValue(ActionEvent JavaDoc e);
567     }
568     
569     /**
570      * Action listener for combo boxes that require setValue to be called
571      * immediately on action event (instead of when focus is gained/lost).
572      */

573     private class ComboBoxActionListener extends FlushActionListener{
574         
575         private final JComboBox comboBox;
576         
577         public ComboBoxActionListener(JComboBox comboBox){
578             this.comboBox = comboBox;
579         }
580         
581         public void doSetValue(ActionEvent JavaDoc e) {
582             setValue(comboBox, comboBox.getSelectedItem());
583         }
584     }
585     
586     /**
587      * Action listener for checkboxes that require setValue to be called
588      * immediately on action event (instead of when focus is gained/lost).
589      */

590     private class CheckBoxActionListener extends FlushActionListener{
591         
592         private final JCheckBox checkBox;
593         
594         public CheckBoxActionListener(JCheckBox checkBox){
595             this.checkBox = checkBox;
596         }
597         
598         public void doSetValue(ActionEvent JavaDoc e) {
599             setValue(checkBox, Boolean.valueOf(checkBox.isSelected()));
600         }
601     }
602     
603     /**
604      * Action listener for radio buttons that require setValue to be called
605      * immediately on action event (instead of when focus is gained/lost).
606      */

607     private class RadioButtonActionListener extends FlushActionListener{
608         
609         private final JRadioButton radioButton;
610         
611         public RadioButtonActionListener(JRadioButton radioButton){
612             this.radioButton = radioButton;
613         }
614         
615         public void doSetValue(ActionEvent JavaDoc e) {
616             setValue(radioButton, Boolean.valueOf(radioButton.isSelected()));
617         }
618     }
619     
620     /**
621      * Listener attached to check boxes that modify the model.
622      */

623     private class CheckBoxModifyFocusListener extends FlushFocusListener {
624         private boolean orgValue;
625         private final JCheckBox checkBox;
626         
627         public CheckBoxModifyFocusListener(JCheckBox checkBox) {
628             this.checkBox = checkBox;
629         }
630         
631         public void focusGained(FocusEvent JavaDoc evt) {
632             orgValue = checkBox.isSelected();
633             activeListener = this;
634         }
635         
636         public void focusLost(FocusEvent JavaDoc evt) {
637             if (!closing) {
638                 flushData();
639                 activeListener = null;
640             }
641         }
642         
643         public boolean flushData() {
644             if (!(checkBox.isSelected() == orgValue)) {
645                 startUIChange();
646                 setValue(checkBox, Boolean.valueOf(checkBox.isSelected()));
647                 signalUIChange();
648                 endUIChange();
649             }
650             return true;
651         }
652         
653         public boolean canClose() {
654             closing = true;
655             try {
656                 if (activeListener != null) {
657                     return activeListener.flushData();
658                 }
659                 return true;
660             } finally {
661                 closing = false;
662             }
663         }
664         
665     }
666     
667     /** This will be called after model is changed from this panel
668      * @deprecated use {@link SectionInnerPanel#endUIChange} instead
669      */

670     protected void signalUIChange() {
671     }
672     
673     /** This will be called before model is changed from this panel
674      */

675     protected void startUIChange() {
676     }
677     
678     /** This will be called after model is changed from this panel
679      */

680     protected void endUIChange() {
681     }
682     
683 }
684
Popular Tags