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.modules.project.ui;
043:
044: import java.awt.BorderLayout;
045: import java.awt.Component;
046: import java.awt.Dialog;
047: import java.awt.Dimension;
048: import java.awt.event.ActionEvent;
049: import java.awt.event.ActionListener;
050: import java.beans.BeanInfo;
051: import java.io.IOException;
052: import java.util.HashSet;
053: import java.util.Iterator;
054: import java.util.ResourceBundle;
055: import java.util.Set;
056: import javax.swing.*;
057: import javax.swing.border.Border;
058: import javax.swing.border.CompoundBorder;
059: import javax.swing.border.EmptyBorder;
060: import javax.swing.border.LineBorder;
061: import javax.swing.event.ListSelectionEvent;
062: import javax.swing.event.ListSelectionListener;
063: import org.openide.DialogDescriptor;
064: import org.openide.DialogDisplayer;
065: import org.openide.ErrorManager;
066: import org.openide.NotifyDescriptor;
067: import org.openide.awt.Mnemonics;
068: import org.openide.cookies.EditorCookie;
069: import org.openide.cookies.SaveCookie;
070: import org.openide.loaders.DataObject;
071: import org.openide.nodes.Node;
072: import org.openide.util.NbBundle;
073:
074: // XXX This code is stolen from core/ExitDialog.
075: /** Dialog which lets the user select which open files to close.
076: *
077: * @author Ian Formanek, Petr Hrebejk
078: */
079: final public class ExitDialog extends JPanel implements ActionListener {
080:
081: /*for tests only!*/static boolean SAVE_ALL_UNCONDITIONALLY = false;
082:
083: private static Object[] exitOptions;
084:
085: /** The dialog */
086: private static Dialog exitDialog;
087:
088: /** Result of the dialog */
089: private static boolean result = false;
090:
091: JList list;
092: DefaultListModel listModel;
093:
094: /** Constructs new dlg for unsaved files in filesystems marked
095: * for unmount.
096: */
097: private ExitDialog(Set<DataObject> openedFiles) {
098: setLayout(new BorderLayout());
099:
100: listModel = new DefaultListModel();
101:
102: Set<DataObject> set = getModifiedFiles(openedFiles);
103: if (!set.isEmpty()) {
104: Iterator iter = set.iterator();
105: while (iter.hasNext()) {
106: DataObject obj = (DataObject) iter.next();
107: listModel.addElement(obj);
108: }
109: draw();
110: }
111: }
112:
113: /** Constructs rest of dialog.
114: */
115: private void draw() {
116: list = new JList(listModel);
117: list.setBorder(new EmptyBorder(2, 2, 2, 2));
118: list.addListSelectionListener(new ListSelectionListener() {
119: public void valueChanged(ListSelectionEvent evt) {
120: updateSaveButton();
121: }
122: });
123: // bugfix 37941, select first item in list
124: if (!listModel.isEmpty()) {
125: list.setSelectedIndex(0);
126: } else {
127: updateSaveButton();
128: }
129: JScrollPane scroll = new JScrollPane(list);
130: scroll.setBorder(new CompoundBorder(new EmptyBorder(12, 12, 11,
131: 0), scroll.getBorder()));
132: add(scroll, BorderLayout.CENTER);
133: list.setCellRenderer(new ExitDlgListCellRenderer());
134: list.getAccessibleContext().setAccessibleName(
135: (NbBundle.getBundle(ExitDialog.class))
136: .getString("ACSN_ListOfChangedFiles"));
137: list.getAccessibleContext().setAccessibleDescription(
138: (NbBundle.getBundle(ExitDialog.class))
139: .getString("ACSD_ListOfChangedFiles"));
140: this .getAccessibleContext().setAccessibleDescription(
141: (NbBundle.getBundle(ExitDialog.class))
142: .getString("ACSD_ExitDialog"));
143: }
144:
145: private void updateSaveButton() {
146: ((JButton) exitOptions[0])
147: .setEnabled(list.getSelectedIndex() != -1);
148: }
149:
150: /** @return preffered size */
151: public Dimension getPreferredSize() {
152: Dimension prev = super .getPreferredSize();
153: return new Dimension(Math.max(300, prev.width), Math.max(150,
154: prev.height));
155: }
156:
157: /** This method is called when is any of buttons pressed
158: */
159: public void actionPerformed(final ActionEvent evt) {
160: if (exitOptions[0].equals(evt.getSource())) {
161: save(false);
162: } else if (exitOptions[1].equals(evt.getSource())) {
163: save(true);
164: } else if (exitOptions[2].equals(evt.getSource())) {
165: theEnd();
166: } else if (NotifyDescriptor.CANCEL_OPTION.equals(evt
167: .getSource())) {
168: exitDialog.setVisible(false);
169: }
170: }
171:
172: /** Save the files from the listbox
173: * @param all true- all files, false - just selected
174: */
175: private void save(boolean all) {
176: Object array[] = ((all) ? listModel.toArray() : list
177: .getSelectedValues());
178: int i, count = ((array == null) ? 0 : array.length);
179: int index = 0; // index of last removed item
180:
181: for (i = 0; i < count; i++) {
182: DataObject nextObject = (DataObject) array[i];
183: index = listModel.indexOf(nextObject);
184: save(nextObject);
185: }
186:
187: if (listModel.isEmpty())
188: theEnd();
189: else { // reset selection to new item at the same index if available
190: if (index < 0)
191: index = 0;
192: else if (index > listModel.size() - 1) {
193: index = listModel.size() - 1;
194: }
195: list.setSelectedIndex(index);
196: }
197: }
198:
199: /** Tries to save given data object using its save cookie.
200: * Notifies user if excetions appear.
201: */
202: private void save(DataObject dataObject) {
203: try {
204: SaveCookie sc = (SaveCookie) dataObject
205: .getCookie(SaveCookie.class);
206: if (sc != null) {
207: sc.save();
208: }
209: listModel.removeElement(dataObject);
210: } catch (IOException exc) {
211: ErrorManager em = ErrorManager.getDefault();
212: Throwable t = em.annotate(exc, NbBundle.getBundle(
213: ExitDialog.class).getString("EXC_Save"));
214: em.notify(ErrorManager.EXCEPTION, t);
215: }
216: }
217:
218: public static void doSave(DataObject dataObject) {
219: try {
220: SaveCookie sc = (SaveCookie) dataObject
221: .getCookie(SaveCookie.class);
222: if (sc != null) {
223: sc.save();
224: }
225: } catch (IOException exc) {
226: ErrorManager em = ErrorManager.getDefault();
227: Throwable t = em.annotate(exc, NbBundle.getBundle(
228: ExitDialog.class).getString("EXC_Save"));
229: em.notify(ErrorManager.EXCEPTION, t);
230: }
231: }
232:
233: /** Exit the IDE
234: */
235: private void theEnd() {
236: // XXX(-ttran) result must be set before calling setVisible(false)
237: // because this will unblock the thread which called Dialog.show()
238:
239: for (int i = listModel.size() - 1; i >= 0; i--) {
240: DataObject obj = (DataObject) listModel.getElementAt(i);
241: obj.setModified(false);
242: }
243:
244: result = true;
245: exitDialog.setVisible(false);
246: exitDialog.dispose();
247: }
248:
249: /** Opens the ExitDialog for unsaved files in filesystems marked
250: * for unmount and blocks until it's closed. If dialog doesm't
251: * exists it creates new one. Returns true if the IDE should be closed.
252: */
253: public static boolean showDialog(Set<DataObject> openedFiles) {
254: return innerShowDialog(getModifiedFiles(openedFiles));
255: }
256:
257: private static Set<DataObject> getModifiedFiles(
258: Set<DataObject> openedFiles) {
259: Set<DataObject> set = new HashSet<DataObject>(openedFiles
260: .size());
261: for (DataObject obj : openedFiles) {
262: if (obj.isModified()) {
263: set.add(obj);
264: }
265: }
266: return set;
267: }
268:
269: /** Opens the ExitDialog for activated nodes or for
270: * whole repository.
271: */
272: private static boolean innerShowDialog(Set<DataObject> openedFiles) {
273: if (!openedFiles.isEmpty()) {
274: if (SAVE_ALL_UNCONDITIONALLY) {
275: //this section should be invoked only by tests!
276: for (DataObject d : openedFiles) {
277: doSave(d);
278: }
279:
280: return true;
281: }
282:
283: // XXX(-ttran) caching this dialog is fatal. If the user
284: // cancels the Exit action, modifies some more files and tries to
285: // Exit again the list of modified DataObject's is not updated,
286: // changes made by the user after the first aborted Exit will be
287: // lost.
288: exitDialog = null;
289:
290: if (exitDialog == null) {
291: ResourceBundle bundle = NbBundle
292: .getBundle(ExitDialog.class);
293: JButton buttonSave = new JButton();
294: buttonSave.getAccessibleContext()
295: .setAccessibleDescription(
296: bundle.getString("ACSD_Save"));
297: JButton buttonSaveAll = new JButton();
298: buttonSaveAll.getAccessibleContext()
299: .setAccessibleDescription(
300: bundle.getString("ACSD_SaveAll"));
301: JButton buttonDiscardAll = new JButton();
302: buttonDiscardAll.getAccessibleContext()
303: .setAccessibleDescription(
304: bundle.getString("ACSD_DiscardAll"));
305:
306: Mnemonics.setLocalizedText(buttonSave, bundle
307: .getString("CTL_Save"));
308: Mnemonics.setLocalizedText(buttonSaveAll, bundle
309: .getString("CTL_SaveAll"));
310: Mnemonics.setLocalizedText(buttonDiscardAll, bundle
311: .getString("CTL_DiscardAll"));
312:
313: exitOptions = new Object[] { buttonSave, buttonSaveAll,
314: buttonDiscardAll, };
315: ExitDialog exitComponent = null;
316: exitComponent = new ExitDialog(openedFiles);
317: DialogDescriptor exitDlgDescriptor = new DialogDescriptor(
318: exitComponent, // inside component
319: bundle.getString("CTL_ExitTitle"), // title
320: true, // modal
321: exitOptions, // options
322: NotifyDescriptor.CANCEL_OPTION, // initial value
323: DialogDescriptor.RIGHT_ALIGN, // option align
324: null, // no help
325: exitComponent // Action Listener
326: );
327: exitDlgDescriptor
328: .setAdditionalOptions(new Object[] { NotifyDescriptor.CANCEL_OPTION });
329: exitDialog = DialogDisplayer.getDefault().createDialog(
330: exitDlgDescriptor);
331: }
332:
333: result = false;
334: exitDialog.setVisible(true); // Show the modal Save dialog
335: return result;
336:
337: } else
338: return true;
339: }
340:
341: /** Renderer used in list box of exit dialog
342: */
343: private class ExitDlgListCellRenderer extends JLabel implements
344: ListCellRenderer {
345:
346: protected Border hasFocusBorder;
347: protected Border noFocusBorder;
348:
349: public ExitDlgListCellRenderer() {
350: this .setOpaque(true);
351: this .setBorder(noFocusBorder);
352: hasFocusBorder = new LineBorder(UIManager
353: .getColor("List.focusCellHighlight")); // NOI18N
354: noFocusBorder = new EmptyBorder(1, 1, 1, 1);
355: }
356:
357: public Component getListCellRendererComponent(JList list,
358: Object value, int index, boolean isSelected,
359: boolean cellHasFocus) // the list and the cell have the focus
360: {
361: final DataObject obj = (DataObject) value;
362: if (!obj.isValid()) {
363: // #17059: it might be invalid already.
364: // #18886: but if so, remove it later, otherwise BasicListUI gets confused.
365: SwingUtilities.invokeLater(new Runnable() {
366: public void run() {
367: listModel.removeElement(obj);
368: }
369: });
370: setText("");
371: return this ;
372: }
373:
374: Node node = obj.getNodeDelegate();
375:
376: ImageIcon icon = new ImageIcon(node
377: .getIcon(BeanInfo.ICON_COLOR_16x16));
378: super .setIcon(icon);
379:
380: setText(node.getDisplayName());
381: if (isSelected) {
382: this .setBackground(UIManager
383: .getColor("List.selectionBackground")); // NOI18N
384: this .setForeground(UIManager
385: .getColor("List.selectionForeground")); // NOI18N
386: } else {
387: this.setBackground(list.getBackground());
388: this.setForeground(list.getForeground());
389: }
390:
391: this.setBorder(cellHasFocus ? hasFocusBorder
392: : noFocusBorder);
393:
394: return this;
395: }
396: }
397: }
|