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.core.spi.multiview;
043:
044: import java.io.Serializable;
045: import java.util.ArrayList;
046: import java.util.Collection;
047: import javax.swing.AbstractAction;
048: import javax.swing.Action;
049: import javax.swing.JComponent;
050: import javax.swing.JPanel;
051: import org.netbeans.core.multiview.MultiViewCloneableTopComponent;
052: import org.netbeans.core.multiview.MultiViewTopComponent;
053: import org.openide.util.Lookup;
054: import org.openide.windows.CloneableTopComponent;
055: import org.openide.windows.TopComponent;
056:
057: /** Factory class for creating top components handling multi views.
058: *
059: * @author Dafe Simonek, Milos Kleint
060: */
061: public final class MultiViewFactory {
062:
063: /**
064: * A utility singleton instance of MultiViewElement that does nothing.
065: */
066:
067: public final static MultiViewElement BLANK_ELEMENT = new Blank();
068: /**
069: * a utility noop action instance to be used when no special handling is
070: * required in createUnsafeCloseState() method.
071: */
072: public final static Action NOOP_CLOSE_ACTION = new NoopAction();
073:
074: /** Factory class, no instances. */
075: private MultiViewFactory() {
076: }
077:
078: /** Creates and returns new instance of top component with
079: * multi views.
080: * PLEASE NOTE: a non-cloneable TopComponent is not able to embed editors aka subclasses of CloneableEditor correctly.
081: * Use createCloneableMultiView() method in such a case.
082: * @param descriptions array of descriptions of tabs in the multiview.
083: * @param defaultDesc the initial selection, one of the descriptions array values.
084: */
085: public static TopComponent createMultiView(
086: MultiViewDescription[] descriptions,
087: MultiViewDescription defaultDesc) {
088: return createMultiView(descriptions, defaultDesc,
089: createDefaultCloseOpHandler());
090: }
091:
092: /** Creates and returns new instance of top component with
093: * multi views.
094: * PLEASE NOTE: a non-cloneable TopComponent is not able to embed editors aka subclasses of CloneableEditor correctly.
095: * Use createCloneableMultiView() method in such a case.
096: * @param descriptions array of descriptions of tabs in the multiview.
097: * @param defaultDesc the initial selection, one of the descriptions array values.
098: * @param closeHandler handles closing of the multiview component, useful when any of the embedded elements can be in modified state and closing would cause a dataloss..
099: */
100: public static TopComponent createMultiView(
101: MultiViewDescription[] descriptions,
102: MultiViewDescription defaultDesc,
103: CloseOperationHandler closeHandler) {
104: if (descriptions == null)
105: return null;
106: if (closeHandler == null)
107: closeHandler = createDefaultCloseOpHandler();
108: MultiViewTopComponent tc = new MultiViewTopComponent();
109: tc.setMultiViewDescriptions(descriptions, defaultDesc);
110: tc.setCloseOperationHandler(closeHandler);
111: return tc;
112: }
113:
114: /** Creates and returns new instance of cloneable top component with
115: * multi views
116: * @param descriptions array of descriptions of tabs in the multiview.
117: * @param defaultDesc the initial selection, one of the descriptions array values.
118: */
119: public static CloneableTopComponent createCloneableMultiView(
120: MultiViewDescription[] descriptions,
121: MultiViewDescription defaultDesc) {
122: return createCloneableMultiView(descriptions, defaultDesc,
123: createDefaultCloseOpHandler());
124: }
125:
126: /** Creates and returns new instance of cloneable top component with
127: * multi views.
128: * @param descriptions array of descriptions of tabs in the multiview.
129: * @param defaultDesc the initial selection, one of the descriptions array values.
130: * @param closeHandler handles closing of the multiview component, useful when any of the embedded elements can be in modified state and closing would cause a dataloss..
131: */
132: public static CloneableTopComponent createCloneableMultiView(
133: MultiViewDescription[] descriptions,
134: MultiViewDescription defaultDesc,
135: CloseOperationHandler closeHandler) {
136: if (descriptions == null)
137: return null;
138: if (closeHandler == null)
139: closeHandler = createDefaultCloseOpHandler();
140: MultiViewCloneableTopComponent tc = new MultiViewCloneableTopComponent();
141: tc.setMultiViewDescriptions(descriptions, defaultDesc);
142: tc.setCloseOperationHandler(closeHandler);
143: return tc;
144: }
145:
146: /**
147: * Utility method for MultiViewElements to create a CloseOperationState instance that
148: * informs the environment that the MVElement is ok to be closed.
149: */
150:
151: static CloseOperationState createSafeCloseState() {
152: return new CloseOperationState(true, "ID_CLOSE_OK",
153: NOOP_CLOSE_ACTION, NOOP_CLOSE_ACTION);
154: }
155:
156: /**
157: * Utility method for MultiViewElements to create a CloseOperationState instance
158: * that warns about possible data loss. Corrective actions can be defined.
159: * @param warningId an id that idenfifies the problem,
160: * the CloseOperationHandler used in the component should know about the warning's meaning and handle appropriately
161: * @param proceedAction will be performed when the CloseOperationHandler decides that closing the component is ok and changes are to be saved.
162: * @param discardAction will be performed when the CloseOperationHandler decides that the nonsaved data shall be discarded
163: */
164:
165: public static CloseOperationState createUnsafeCloseState(
166: String warningId, Action proceedAction, Action discardAction) {
167: return new CloseOperationState(false, (warningId == null ? ""
168: : warningId),
169: (proceedAction == null ? NOOP_CLOSE_ACTION
170: : proceedAction),
171: (discardAction == null ? NOOP_CLOSE_ACTION
172: : discardAction));
173: }
174:
175: static CloseOperationHandler createDefaultCloseOpHandler() {
176: return new DefaultCloseHandler();
177: }
178:
179: private static final class Blank implements MultiViewElement,
180: Serializable {
181:
182: private JPanel panel;
183: private JPanel bar;
184:
185: Blank() {
186: panel = new JPanel();
187: bar = new JPanel();
188: }
189:
190: public void componentActivated() {
191: }
192:
193: public void componentClosed() {
194: }
195:
196: public void componentDeactivated() {
197: }
198:
199: public void componentHidden() {
200: }
201:
202: public void componentOpened() {
203: }
204:
205: public void componentShowing() {
206: }
207:
208: public Action[] getActions() {
209: return new Action[0];
210: }
211:
212: public Lookup getLookup() {
213: return Lookup.EMPTY;
214: }
215:
216: public JComponent getToolbarRepresentation() {
217: return bar;
218: }
219:
220: public javax.swing.JComponent getVisualRepresentation() {
221: return panel;
222: }
223:
224: public void setMultiViewCallback(
225: MultiViewElementCallback callback) {
226: }
227:
228: public org.openide.awt.UndoRedo getUndoRedo() {
229: return null;
230: }
231:
232: public CloseOperationState canCloseElement() {
233: return CloseOperationState.STATE_OK;
234: }
235:
236: }
237:
238: /**
239: * default simple implementation of the close handler.
240: */
241: private static final class DefaultCloseHandler implements
242: CloseOperationHandler, Serializable {
243: private static final long serialVersionUID = -3126744916624172427L;
244:
245: public boolean resolveCloseOperation(
246: CloseOperationState[] elements) {
247: if (elements != null) {
248: boolean canBeClosed = true;
249: Collection badOnes = new ArrayList();
250: for (int i = 0; i < elements.length; i++) {
251: if (!elements[i].canClose()) {
252: badOnes.add(elements[i]);
253: canBeClosed = false;
254: }
255: }
256: if (!canBeClosed) {
257: //TODO SHOW dialog here.
258: throw new IllegalStateException(
259: "Cannot close component. Some of the elements require close operation handling. See MultiViewFactory.createMultiView()");
260: // Object[] options = new Object[] {
261: // new JButton("Proceed"),
262: // new JButton("Discard"),
263: // new JButton("Cancel")
264: // };
265: // NotifyDescriptor desc = new NotifyDescriptor(createPanel(badOnes), "Cannot close component.",
266: // NotifyDescriptor.DEFAULT_OPTION, NotifyDescriptor.WARNING_MESSAGE,
267: // options, options[0]);
268: // Object retVal = DialogDisplayer.getDefault().notify(desc);
269: // if (retVal == options[0]) {
270: // // do proceed.
271: // Iterator it = badOnes.iterator();
272: // while (it.hasNext()) {
273: // Action act = ((CloseOperationState)it.next()).getProceedAction();
274: // if (act != null) {
275: // act.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "proceed"));
276: // }
277: // }
278: // } else if (retVal == options[1]) {
279: // // do discard
280: // Iterator it = badOnes.iterator();
281: // while (it.hasNext()) {
282: // Action act = ((CloseOperationState)it.next()).getDiscardAction();
283: // if (act != null) {
284: // act.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "discard"));
285: // }
286: // }
287: // } else {
288: // // was cancel..
289: // return false;
290: // }
291: }
292: }
293: return true;
294: }
295:
296: // private JPanel createPanel(Collection elems) {
297: // JPanel panel = new JPanel();
298: // panel.setLayout(new BorderLayout());
299: // JLabel lbl = new JLabel("Cannot safely close component for following reasons:");
300: // panel.add(lbl, BorderLayout.NORTH);
301: // JScrollPane pane = new JScrollPane();
302: // String[] warnings = new String[elems.size()];
303: // int index = 0;
304: // Iterator it = elems.iterator();
305: // while (it.hasNext()) {
306: // CloseOperationState state = (CloseOperationState)it.next();
307: // warnings[index] = state.getCloseWarningMessage();
308: // index = index + 1;
309: // }
310: // JList list = new JList(warnings);
311: // pane.setViewportView(list);
312: // panel.add(pane);
313: // return panel;
314: // }
315: }
316:
317: /**
318: * just a default noon action to put into the closeoperation state.
319: */
320: private static final class NoopAction extends AbstractAction {
321:
322: public void actionPerformed(java.awt.event.ActionEvent e) {
323: // do nothing
324: }
325:
326: }
327:
328: }
|