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