001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2008 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-2008 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.ruby.platform;
042:
043: import java.awt.Color;
044: import java.awt.Component;
045: import java.awt.EventQueue;
046: import java.awt.event.ItemEvent;
047: import java.awt.event.ItemListener;
048: import java.beans.PropertyChangeEvent;
049: import java.beans.PropertyChangeListener;
050: import java.io.File;
051: import java.io.IOException;
052: import java.text.Collator;
053: import java.util.Arrays;
054: import java.util.Comparator;
055: import java.util.Set;
056: import java.util.logging.Level;
057: import java.util.logging.Logger;
058: import javax.swing.AbstractListModel;
059: import javax.swing.ComboBoxModel;
060: import javax.swing.DefaultComboBoxModel;
061: import javax.swing.JComboBox;
062: import javax.swing.JLabel;
063: import javax.swing.JList;
064: import javax.swing.ListCellRenderer;
065: import javax.swing.ListSelectionModel;
066: import javax.swing.UIManager;
067: import javax.swing.plaf.UIResource;
068: import org.netbeans.api.ruby.platform.RubyPlatform;
069: import org.netbeans.api.ruby.platform.RubyPlatformManager;
070: import org.openide.util.NbBundle;
071: import org.openide.util.RequestProcessor;
072:
073: public final class PlatformComponentFactory {
074:
075: /** Generally usable in conjuction with {@link #createComboWaitModel}. */
076: private static final String DETECTING_VALUE = NbBundle.getMessage(
077: PlatformComponentFactory.class,
078: "PlatformComponentFactory.detetctingPlatforms");
079:
080: private static final Logger LOGGER = Logger
081: .getLogger(PlatformComponentFactory.class.getName());
082:
083: public static final Color INVALID_PLAF_COLOR = UIManager
084: .getColor("nb.errorForeground"); // NOI18N
085:
086: private PlatformComponentFactory() {
087: // don't allow instances
088: }
089:
090: /**
091: * Returns <code>JComboBox</code> initialized with {@link
092: * RubyPlatformListModel} which contains all Ruby platform.
093: */
094: public static JComboBox getRubyPlatformsComboxBox() {
095: final JComboBox plafComboBox = new JComboBox();
096: plafComboBox.setRenderer(new RubyPlatformListRenderer());
097: if (Util.isFirstPlatformTouch()) {
098: plafComboBox.setModel(createComboWaitModel());
099: plafComboBox.setEnabled(false);
100: RequestProcessor.getDefault().post(new Runnable() {
101: public void run() {
102: RubyPlatformManager.performPlatformDetection();
103: EventQueue.invokeLater(new Runnable() {
104: public void run() {
105: plafComboBox
106: .setModel(new RubyPlatformListModel());
107: plafComboBox.setEnabled(true);
108: }
109: });
110: }
111: });
112: } else {
113: plafComboBox.setModel(new RubyPlatformListModel());
114: }
115: return plafComboBox;
116: }
117:
118: public static RubyPlatform getPlatform(final JComboBox platforms) {
119: Object value = platforms.getModel().getSelectedItem();
120: return (value == DETECTING_VALUE) ? null : (RubyPlatform) value;
121: }
122:
123: public static boolean isLoadingPlatforms(JComboBox platforms) {
124: Object value = platforms.getModel().getSelectedItem();
125: return value == DETECTING_VALUE;
126: }
127:
128: /**
129: * Returns <code>JList</code> initialized with {@link RubyPlatformListModel}
130: * which contains all Ruby platform.
131: */
132: public static JList getRubyPlatformsList() {
133: JList plafList = new JList(new RubyPlatformListModel());
134: plafList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
135: plafList.setCellRenderer(new RubyPlatformListRenderer());
136: return plafList;
137: }
138:
139: /**
140: * Use this model in situation when you need to populate combo in the
141: * background. The only item in this model is {@link #WAIT_VALUE}.
142: */
143: public static ComboBoxModel createComboWaitModel() {
144: return new DefaultComboBoxModel(
145: new Object[] { DETECTING_VALUE });
146: }
147:
148: public static void addPlatformChangeListener(
149: final JComboBox platforms, final PlatformChangeListener pcl) {
150: platforms.addItemListener(pcl);
151: platforms.addPropertyChangeListener(pcl);
152: }
153:
154: public static void removePlatformChangeListener(
155: final JComboBox platforms, final PlatformChangeListener pcl) {
156: platforms.removeItemListener(pcl);
157: platforms.removePropertyChangeListener(pcl);
158: }
159:
160: public static abstract class PlatformChangeListener implements
161: ItemListener, PropertyChangeListener {
162:
163: public abstract void platformChanged();
164:
165: public void itemStateChanged(ItemEvent e) {
166: platformChanged();
167: }
168:
169: public void propertyChange(PropertyChangeEvent evt) {
170: // when the model has changed from "Detectin platform" to valid
171: // platform model itemStateChanged is not fired(??)
172: if (evt.getPropertyName().equals("model")) { // NOI18N
173: platformChanged();
174: }
175: }
176: }
177:
178: /**
179: * Returns model containing all <em>currently</em> registered RubyPlatforms.
180: * See also {@link RubyPlatform#getPlatforms}.
181: * <p>Use in conjuction with {@link RubyPlatformListRenderer}</p>
182: */
183: public static class RubyPlatformListModel extends AbstractListModel
184: implements ComboBoxModel {
185:
186: private static RubyPlatform[] getSortedPlatforms(
187: RubyPlatform extra) {
188: Set<RubyPlatform> _platforms = RubyPlatformManager
189: .getPlatforms();
190: if (extra != null) {
191: _platforms.add(extra);
192: }
193: RubyPlatform[] platforms = _platforms
194: .toArray(new RubyPlatform[_platforms.size()]);
195: Arrays.sort(platforms, new Comparator<RubyPlatform>() {
196: public int compare(RubyPlatform p1, RubyPlatform p2) {
197: int res = Collator.getInstance().compare(
198: p1.getInfo().getLongDescription(),
199: p2.getInfo().getLongDescription());
200: if (res != 0) {
201: return res;
202: } else {
203: return System.identityHashCode(p1)
204: - System.identityHashCode(p2);
205: }
206: }
207: });
208: return platforms;
209: }
210:
211: private RubyPlatform[] nbPlafs;
212: private Object selectedPlaf;
213:
214: public RubyPlatformListModel() {
215: this (null);
216: }
217:
218: public RubyPlatformListModel(
219: final RubyPlatform initiallySelected) {
220: nbPlafs = getSortedPlatforms(initiallySelected);
221: if (initiallySelected == null) {
222: if (nbPlafs.length > 0) {
223: selectedPlaf = nbPlafs[0];
224: }
225: } else {
226: selectedPlaf = initiallySelected;
227: }
228: }
229:
230: public int getSize() {
231: return nbPlafs.length;
232: }
233:
234: public Object getElementAt(int index) {
235: return index < nbPlafs.length ? nbPlafs[index] : null;
236: }
237:
238: public void setSelectedItem(Object plaf) {
239: assert plaf == null || plaf instanceof RubyPlatform;
240: if (selectedPlaf != plaf) {
241: selectedPlaf = plaf;
242: fireContentsChanged(this , -1, -1);
243: }
244: }
245:
246: public Object getSelectedItem() {
247: return selectedPlaf;
248: }
249:
250: void removePlatform(RubyPlatform plaf) {
251: try {
252: RubyPlatformManager.removePlatform(plaf);
253: nbPlafs = getSortedPlatforms(null); // refresh
254: fireContentsChanged(this , 0, nbPlafs.length - 1);
255: } catch (IOException e) {
256: // tell the user that something goes wrong
257: LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
258: }
259: }
260:
261: RubyPlatform addPlatform(final File interpreter) {
262: try {
263: RubyPlatform platform = RubyPlatformManager
264: .addPlatform(interpreter);
265: if (platform != null) {
266: nbPlafs = getSortedPlatforms(null); // refresh
267: fireContentsChanged(this , 0, nbPlafs.length - 1);
268: }
269: return platform;
270: } catch (IOException e) {
271: // tell the user that something goes wrong
272: LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
273: }
274: return null;
275: }
276: }
277:
278: /**
279: * Render {@link RubyPlatform}.
280: * <p>Use in conjuction with {@link RubyPlatformListModel}</p>
281: */
282: private static class RubyPlatformListRenderer extends JLabel
283: implements ListCellRenderer, UIResource {
284:
285: public RubyPlatformListRenderer() {
286: setOpaque(true);
287: }
288:
289: public Component getListCellRendererComponent(JList list,
290: Object value, int index, boolean isSelected,
291: boolean cellHasFocus) {
292: // #93658: GTK needs name to render cell renderer "natively"
293: setName("ComboBox.listRenderer"); // NOI18N
294:
295: if (isSelected) {
296: setBackground(list.getSelectionBackground());
297: setForeground(list.getSelectionForeground());
298: } else {
299: setBackground(list.getBackground());
300: setForeground(list.getForeground());
301: }
302:
303: String label;
304: if (value instanceof String) {
305: label = (String) value;
306: } else {
307: RubyPlatform plaf = ((RubyPlatform) value);
308: label = plaf.getLabel();
309: if (plaf != null && !plaf.isValid()) {
310: setForeground(INVALID_PLAF_COLOR);
311: }
312: }
313: setText(label);
314:
315: return this ;
316: }
317:
318: // #93658: GTK needs name to render cell renderer "natively"
319: public @Override
320: String getName() {
321: String name = super .getName();
322: return name == null ? "ComboBox.renderer" : name; // NOI18N
323: }
324: }
325:
326: }
|