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: package org.netbeans.modules.visualweb.insync;
042:
043: import com.sun.rave.designtime.markup.MarkupDesignBean;
044: import java.awt.Component;
045: import java.awt.event.ActionEvent;
046: import java.awt.event.ActionListener;
047: import java.beans.PropertyChangeEvent;
048: import java.beans.PropertyChangeListener;
049:
050: import javax.swing.JButton;
051:
052: import org.openide.DialogDescriptor;
053: import org.openide.DialogDisplayer;
054: import org.openide.util.HelpCtx;
055: import org.openide.util.NbBundle;
056:
057: import com.sun.rave.designtime.Result;
058: import com.sun.rave.designtime.DesignBean;
059: import com.sun.rave.designtime.Customizer2;
060: import javax.swing.JDialog;
061: import org.netbeans.modules.visualweb.insync.models.FacesModel;
062:
063: /**
064: * Class responsible for handling live customizers. It will display a live customizer, and also
065: * suppress document updates on the bean's element (and its descendants) while the dialog is
066: * visible.
067: * @todo Transfer the plugin thing to the DesignerService?
068: *
069: * @author Tor Norbye
070: */
071: public class CustomizerDisplayer implements ActionListener,
072: PropertyChangeListener {
073:
074: /**
075: * Interface by listeners to batch editing operations of a DesignBean. The intended use for this
076: * is for example the designer listening to a DataTable getting customized; when we apply the
077: * changes in the data table, we want only a single designer update of the table, not continuous
078: * incremental updates as the data table customizer walks over the table structure and adds and
079: * deletes rows.
080: */
081: public interface BatchListener {
082: /** The given DesignBean is about to be batch modified */
083: public void beanModifyBegin(MarkupDesignBean bean,
084: FacesModel model);
085:
086: /** The given DesignBean is done getting modified */
087: public void beanModifyEnd(MarkupDesignBean bean,
088: FacesModel model);
089: }
090:
091: private DesignBean bean;
092: private Customizer2 customizer;
093: private String help;
094: private DialogDescriptor descriptor;
095: private JButton applyButton;
096: private JButton closeButton;
097: private FacesModel model;
098:
099: public CustomizerDisplayer(DesignBean bean, Customizer2 customizer,
100: String help, FacesModel model) {
101: this .bean = bean;
102: this .customizer = customizer;
103: this .help = help;
104: this .model = model;
105: }
106:
107: /**
108: * Show the given customizer
109: *
110: * @param bean The bean to be customized
111: * @param customizer The customizer to customize it with
112: * @param help A help topic id, if any
113: */
114: public void show() {
115: if (customizer == null) {
116: return;
117: }
118: Component panel = customizer.getCustomizerPanel(bean);
119: if (panel == null) {
120: return;
121: }
122:
123: HelpCtx helpCtx = null;
124: if ((help != null) && (help.length() > 0)) {
125: helpCtx = new HelpCtx(help);
126: }
127:
128: Object[] options = null;
129: Object[] closingOptions = null;
130: if (customizer.isApplyCapable()) {
131: applyButton = new JButton(NbBundle.getMessage(
132: CustomizerDisplayer.class, "Apply")); // NOI18N
133: applyButton.setMnemonic(NbBundle.getMessage(
134: CustomizerDisplayer.class, "Apply_mnemonic")
135: .charAt(0));
136: applyButton.getAccessibleContext().setAccessibleName(
137: NbBundle.getMessage(CustomizerDisplayer.class,
138: "APPLY_ACCESS_NAME"));
139: applyButton.getAccessibleContext()
140: .setAccessibleDescription(
141: NbBundle.getMessage(
142: CustomizerDisplayer.class,
143: "APPLY_ACCESS_NAME"));
144: boolean enabled = customizer.isModified(); // in case changes aren't cached
145: applyButton.setEnabled(enabled);
146:
147: options = new Object[] { DialogDescriptor.OK_OPTION,
148: DialogDescriptor.CANCEL_OPTION, applyButton, };
149: closingOptions = new Object[] { DialogDescriptor.OK_OPTION,
150: DialogDescriptor.CANCEL_OPTION };
151: } else {
152: closeButton = new JButton(NbBundle.getMessage(
153: CustomizerDisplayer.class, "Close")); // NOI18N
154: closeButton.setMnemonic(NbBundle.getMessage(
155: CustomizerDisplayer.class, "Close_mnemonic")
156: .charAt(0));
157: closeButton.getAccessibleContext().setAccessibleName(
158: NbBundle.getMessage(CustomizerDisplayer.class,
159: "CLOSE_ACCESS_NAME"));
160: closeButton.getAccessibleContext()
161: .setAccessibleDescription(
162: NbBundle.getMessage(
163: CustomizerDisplayer.class,
164: "CLOSE_ACCESS_DESC"));
165: options = new Object[] { closeButton };
166: closingOptions = options;
167: }
168:
169: descriptor = new DialogDescriptor(panel, customizer
170: .getDisplayName(), // title
171: true, // modal
172: options, DialogDescriptor.OK_OPTION, // default
173: DialogDescriptor.DEFAULT_ALIGN, helpCtx, this );
174: descriptor.setClosingOptions(closingOptions);
175: customizer.addPropertyChangeListener(this );
176: JDialog dialog = (JDialog) DialogDisplayer.getDefault()
177: .createDialog(descriptor);
178: dialog.getAccessibleContext().setAccessibleDescription(
179: customizer.getDisplayName());
180: dialog.show();
181: }
182:
183: public final void propertyChange(PropertyChangeEvent evt) {
184: // Update valid state for each of the buttons
185: boolean enabled = customizer.isModified();
186: if (applyButton != null) {
187: applyButton.setEnabled(enabled);
188: }
189: }
190:
191: /** Invoked when the user presses a button, or presses Esc, etc. */
192: public void actionPerformed(ActionEvent evt) {
193: if (descriptor == null) {
194: return; // impossible?
195: }
196: Object chosen = descriptor.getValue();
197: if (chosen == DialogDescriptor.OK_OPTION
198: || chosen == closeButton || chosen == applyButton) {
199: try {
200: startTask();
201: Result r = customizer.applyChanges();
202: ResultHandler.handleResult(r, model);
203: } finally {
204: finishTask();
205: }
206: if (applyButton != null) {
207: boolean enabled = customizer.isModified(); // in case changes aren't cached
208: applyButton.setEnabled(enabled);
209: }
210: } else {
211: // Could be cancel, could be "escape", etc.
212: }
213: if (chosen == DialogDescriptor.OK_OPTION
214: || chosen == closeButton
215: || chosen == DialogDescriptor.CANCEL_OPTION) {
216: // Dialog is not coming back:
217: customizer.removePropertyChangeListener(this );
218: }
219: }
220:
221: private UndoEvent undoEvent = null;
222:
223: private void startTask() {
224: undoEvent = model.writeLock(NbBundle.getMessage(
225: CustomizerDisplayer.class, "Customize")); // NOI18N
226: if (bean instanceof MarkupDesignBean) {
227: batchModifyBean((MarkupDesignBean) bean, true);
228: }
229: }
230:
231: private void finishTask() {
232: if (undoEvent != null) {
233: model.writeUnlock(undoEvent);
234: undoEvent = null;
235: }
236: if (bean instanceof MarkupDesignBean) {
237: batchModifyBean((MarkupDesignBean) bean, false);
238: }
239: }
240:
241: /**
242: * Notify clients that the given bean is about to be "batch modified". This allows for example
243: * the designer to filter editing events in a more clever way.
244: *
245: * @param bean
246: * @param b
247: * @todo Generated comment
248: */
249: private void batchModifyBean(MarkupDesignBean bean, boolean b) {
250: if (batchListener != null) {
251: if (b) {
252: batchListener.beanModifyBegin(bean, model);
253: } else {
254: batchListener.beanModifyEnd(bean, model);
255: }
256: }
257: }
258:
259: /**
260: * Register a listener to be notified when a DesignBean is about to be batch modified.
261: *
262: * @todo Switch over the a listener list, with add and remove listener methods, etc.
263: */
264: public static void setBatchListener(BatchListener batchListener) {
265: if (CustomizerDisplayer.batchListener != null) {
266: throw new UnsupportedOperationException(
267: "There can only be one batch listener"); // NOI18N - internal error only
268: }
269: CustomizerDisplayer.batchListener = batchListener;
270: }
271:
272: private static BatchListener batchListener;
273:
274: }
|