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 General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.wizard.components.panels;
038:
039: import java.awt.Component;
040: import java.awt.GridBagConstraints;
041: import java.awt.Insets;
042: import java.awt.event.ActionEvent;
043: import java.awt.event.ActionListener;
044: import java.io.File;
045: import java.util.LinkedList;
046: import java.util.List;
047: import javax.swing.ComboBoxEditor;
048: import javax.swing.ComboBoxModel;
049: import javax.swing.JComponent;
050: import javax.swing.JFileChooser;
051: import javax.swing.JLabel;
052: import javax.swing.JList;
053: import javax.swing.ListCellRenderer;
054: import javax.swing.ListModel;
055: import javax.swing.border.EmptyBorder;
056: import javax.swing.event.DocumentEvent;
057: import javax.swing.event.DocumentListener;
058: import javax.swing.event.ListDataEvent;
059: import javax.swing.event.ListDataListener;
060: import javax.swing.event.ListSelectionEvent;
061: import javax.swing.event.ListSelectionListener;
062: import org.netbeans.installer.utils.ResourceUtils;
063: import org.netbeans.installer.utils.StringUtils;
064: import org.netbeans.installer.utils.SystemUtils;
065: import org.netbeans.installer.utils.helper.swing.NbiButton;
066: import org.netbeans.installer.utils.helper.swing.NbiDirectoryChooser;
067: import org.netbeans.installer.utils.helper.swing.NbiLabel;
068: import org.netbeans.installer.utils.helper.swing.NbiList;
069: import org.netbeans.installer.utils.helper.swing.NbiPanel;
070: import org.netbeans.installer.utils.helper.swing.NbiScrollPane;
071: import org.netbeans.installer.utils.helper.swing.NbiTextField;
072: import org.netbeans.installer.wizard.ui.SwingUi;
073: import org.netbeans.installer.wizard.ui.WizardUi;
074: import org.netbeans.installer.wizard.containers.SwingContainer;
075:
076: /**
077: *
078: * @author Kirill Sorokin
079: */
080: public abstract class ApplicationLocationPanel extends
081: ErrorMessagePanel {
082: /////////////////////////////////////////////////////////////////////////////////
083: // Instance
084: public ApplicationLocationPanel() {
085: setProperty(LOCATION_LABEL_TEXT_PROPERTY,
086: DEFAULT_LOCATION_LABEL_TEXT);
087: setProperty(LOCATION_BUTTON_TEXT_PROPERTY,
088: DEFAULT_LOCATION_BUTTON_TEXT);
089: setProperty(LIST_LABEL_TEXT_PROPERTY, DEFAULT_LIST_LABEL_TEXT);
090:
091: setProperty(ERROR_NOTHING_FOUND_PROPERTY,
092: DEFAULT_ERROR_NOTHING_FOUND);
093: }
094:
095: public abstract List<File> getLocations();
096:
097: public abstract List<String> getLabels();
098:
099: public abstract File getSelectedLocation();
100:
101: public abstract String validateLocation(String value);
102:
103: public abstract void setLocation(File location);
104:
105: @Override
106: public WizardUi getWizardUi() {
107: if (wizardUi == null) {
108: wizardUi = new ApplicationLocationPanelUi(this );
109: }
110:
111: return wizardUi;
112: }
113:
114: /////////////////////////////////////////////////////////////////////////////////
115: // Inner Classes
116: public static class ApplicationLocationPanelUi extends
117: ErrorMessagePanelUi {
118: protected ApplicationLocationPanel component;
119:
120: public ApplicationLocationPanelUi(
121: final ApplicationLocationPanel component) {
122: super (component);
123:
124: this .component = component;
125: }
126:
127: public SwingUi getSwingUi(SwingContainer container) {
128: if (swingUi == null) {
129: swingUi = new ApplicationLocationPanelSwingUi(
130: component, container);
131: }
132:
133: return super .getSwingUi(container);
134: }
135: }
136:
137: public static class ApplicationLocationPanelSwingUi extends
138: ErrorMessagePanelSwingUi {
139: private ApplicationLocationPanel component;
140:
141: private NbiLabel locationLabel;
142: private NbiTextField locationField;
143: private NbiButton locationButton;
144:
145: private NbiLabel locationsLabel;
146: private NbiList locationsList;
147: private NbiScrollPane locationsScrollPane;
148:
149: private NbiPanel locationsListReplacement;
150:
151: private NbiDirectoryChooser fileChooser;
152:
153: public ApplicationLocationPanelSwingUi(
154: final ApplicationLocationPanel component,
155: final SwingContainer container) {
156: super (component, container);
157:
158: this .component = component;
159:
160: initComponents();
161: }
162:
163: @Override
164: public JComponent getDefaultFocusOwner() {
165: return locationField;
166: }
167:
168: // protected ////////////////////////////////////////////////////////////////
169: @Override
170: protected void initialize() {
171: super .initialize();
172:
173: locationLabel.setText(component
174: .getProperty(LOCATION_LABEL_TEXT_PROPERTY));
175: locationButton.setText(component
176: .getProperty(LOCATION_BUTTON_TEXT_PROPERTY));
177: locationsLabel.setText(component
178: .getProperty(LIST_LABEL_TEXT_PROPERTY));
179:
180: LocationsListModel model = new LocationsListModel(component
181: .getLocations(), component.getLabels());
182: locationsList.setModel(model);
183:
184: File selectedLocation = component.getSelectedLocation();
185: if (model.getSize() > 0) {
186: if (selectedLocation != null) {
187: locationField.setText(selectedLocation
188: .getAbsolutePath());
189: } else {
190: locationField.setText(model.getLocationAt(0)
191: .getAbsolutePath());
192: }
193:
194: locationsLabel.setVisible(true);
195: locationsScrollPane.setVisible(true);
196: locationsListReplacement.setVisible(false);
197: } else {
198: if (selectedLocation != null) {
199: locationField.setText(selectedLocation
200: .getAbsolutePath());
201: } else {
202: locationField.setText(SystemUtils.resolvePath(
203: DEFAULT_LOCATION).getAbsolutePath());
204: }
205:
206: locationsLabel.setVisible(false);
207: locationsScrollPane.setVisible(false);
208: locationsListReplacement.setVisible(true);
209: }
210:
211: updateErrorMessage();
212: }
213:
214: @Override
215: protected void saveInput() {
216: component.setLocation(new File(locationField.getText()
217: .trim()));
218: }
219:
220: @Override
221: protected String validateInput() {
222: return component.validateLocation(locationField.getText()
223: .trim());
224: }
225:
226: // private //////////////////////////////////////////////////////////////////
227: private void initComponents() {
228: // locationField ////////////////////////////////////////////////////////
229: locationField = new NbiTextField();
230: locationField.getDocument().addDocumentListener(
231: new DocumentListener() {
232: public void changedUpdate(
233: final DocumentEvent event) {
234: locationChanged();
235: }
236:
237: public void insertUpdate(
238: final DocumentEvent event) {
239: locationChanged();
240: }
241:
242: public void removeUpdate(
243: final DocumentEvent event) {
244: locationChanged();
245: }
246: });
247:
248: // locationLabel ////////////////////////////////////////////////////////
249: locationLabel = new NbiLabel();
250: locationLabel.setLabelFor(locationField);
251:
252: // locationButton ///////////////////////////////////////////////////////
253: locationButton = new NbiButton();
254: locationButton.addActionListener(new ActionListener() {
255: public void actionPerformed(ActionEvent event) {
256: browseButtonPressed();
257: }
258: });
259:
260: // locationsList ////////////////////////////////////////////////////////
261: locationsList = new NbiList();
262: locationsList.setBorder(new EmptyBorder(0, 0, 0, 0));
263: locationsList
264: .setCellRenderer(new LocationsListCellRenderer());
265: locationsList
266: .addListSelectionListener(new ListSelectionListener() {
267: public void valueChanged(
268: ListSelectionEvent event) {
269: if (!event.getValueIsAdjusting()) {
270: listSelectionChanged();
271: }
272: }
273: });
274:
275: // locationsScrollPane //////////////////////////////////////////////////
276: locationsScrollPane = new NbiScrollPane(locationsList);
277:
278: // locationsLabel ///////////////////////////////////////////////////////
279: locationsLabel = new NbiLabel();
280: locationsLabel.setLabelFor(locationsList);
281:
282: // locationsListReplacement /////////////////////////////////////////////
283: locationsListReplacement = new NbiPanel();
284:
285: // fileChooser //////////////////////////////////////////////////////////
286: fileChooser = new NbiDirectoryChooser();
287: // this /////////////////////////////////////////////////////////////////
288: add(locationLabel, new GridBagConstraints(0, 1, // x, y
289: 2, 1, // width, height
290: 1.0, 0.0, // weight-x, weight-y
291: GridBagConstraints.LINE_START, // anchor
292: GridBagConstraints.HORIZONTAL, // fill
293: new Insets(7, 11, 0, 11), // padding
294: 0, 0)); // padx, pady - ???
295: add(locationField, new GridBagConstraints(0, 2, // x, y
296: 1, 1, // width, height
297: 1.0, 0.0, // weight-x, weight-y
298: GridBagConstraints.LINE_START, // anchor
299: GridBagConstraints.HORIZONTAL, // fill
300: new Insets(4, 11, 0, 4), // padding
301: 0, 0)); // padx, pady - ???
302: add(locationButton, new GridBagConstraints(1, 2, // x, y
303: 1, 1, // width, height
304: 0.0, 0.0, // weight-x, weight-y
305: GridBagConstraints.LINE_START, // anchor
306: GridBagConstraints.NONE, // fill
307: new Insets(4, 0, 0, 11), // padding
308: 0, 0)); // padx, pady - ???
309: add(locationsLabel, new GridBagConstraints(0, 3, // x, y
310: 2, 1, // width, height
311: 1.0, 0.0, // weight-x, weight-y
312: GridBagConstraints.LINE_START, // anchor
313: GridBagConstraints.HORIZONTAL, // fill
314: new Insets(11, 11, 0, 11), // padding
315: 0, 0)); // padx, pady - ???
316: add(locationsScrollPane, new GridBagConstraints(0, 4, // x, y
317: 2, 1, // width, height
318: 1.0, 1.0, // weight-x, weight-y
319: GridBagConstraints.CENTER, // anchor
320: GridBagConstraints.BOTH, // fill
321: new Insets(4, 11, 0, 11), // padding
322: 0, 0)); // padx, pady - ???
323: add(locationsListReplacement, new GridBagConstraints(0, 5, // x, y
324: 2, 1, // width, height
325: 1.0, 1.0, // weight-x, weight-y
326: GridBagConstraints.CENTER, // anchor
327: GridBagConstraints.BOTH, // fill
328: new Insets(4, 11, 0, 11), // padding
329: 0, 0)); // padx, pady - ???
330: }
331:
332: private void browseButtonPressed() {
333: fileChooser.setSelectedFile(new File(locationField
334: .getText()));
335:
336: if (fileChooser.showOpenDialog(this ) == JFileChooser.APPROVE_OPTION) {
337: locationField.setText(fileChooser.getSelectedFile()
338: .getAbsolutePath());
339: }
340: }
341:
342: private void listSelectionChanged() {
343: final LocationsListModel model = (LocationsListModel) locationsList
344: .getModel();
345:
346: final int index = locationsList.getSelectedIndex();
347: if (index != -1) {
348: String location = model.getLocationAt(index)
349: .getAbsolutePath();
350: if (!location.equals(locationField.getText())) {
351: locationField.setText(location);
352: }
353: }
354: }
355:
356: private void locationChanged() {
357: updateErrorMessage();
358:
359: final LocationsListModel model = (LocationsListModel) locationsList
360: .getModel();
361: final String value = locationField.getText().trim();
362:
363: for (int i = 0; i < model.getSize(); i++) {
364: final String element = (String) model.getLocationAt(i)
365: .getAbsolutePath();
366:
367: if (value.equals(element)) {
368: locationsList.setSelectedIndex(i);
369: return;
370: }
371: }
372:
373: locationsList.clearSelection();
374: }
375: }
376:
377: public static class LocationsListCellRenderer extends JLabel
378: implements ListCellRenderer {
379: public LocationsListCellRenderer() {
380: setBorder(new EmptyBorder(3, 3, 3, 3));
381: }
382:
383: public Component getListCellRendererComponent(JList list,
384: Object value, int index, boolean isSelected,
385: boolean cellHasFocus) {
386: setText(value.toString());
387: setToolTipText(value.toString());
388:
389: if (isSelected) {
390: setBackground(list.getSelectionBackground());
391: setForeground(list.getSelectionForeground());
392: setOpaque(true);
393: } else {
394: setBackground(list.getBackground());
395: setForeground(list.getForeground());
396: setOpaque(false);
397: }
398:
399: return this ;
400: }
401: }
402:
403: public static class LocationsListModel implements ListModel {
404: private List<File> locations;
405: private List<String> labels;
406:
407: public LocationsListModel(final List<File> locations,
408: final List<String> labels) {
409: this .locations = locations;
410: this .labels = labels;
411: }
412:
413: public int getSize() {
414: return locations.size();
415: }
416:
417: public Object getElementAt(int index) {
418: return getLabelAt(index);
419: }
420:
421: public String getLabelAt(int index) {
422: return labels.get(index);
423: }
424:
425: public File getLocationAt(int index) {
426: return locations.get(index);
427: }
428:
429: public void addListDataListener(ListDataListener listener) {
430: // does nothing
431: }
432:
433: public void removeListDataListener(ListDataListener listener) {
434: // does nothing
435: }
436: }
437:
438: public static class LocationsComboBoxEditor implements
439: ComboBoxEditor {
440: private NbiTextField textField;
441:
442: private LocationValidator validator;
443: private LocationsComboBoxModel model;
444:
445: public LocationsComboBoxEditor(LocationValidator validator) {
446: this .validator = validator;
447:
448: textField = new NbiTextField();
449: textField.getDocument().addDocumentListener(
450: new DocumentListener() {
451: public void insertUpdate(DocumentEvent e) {
452: LocationsComboBoxEditor.this .validator
453: .validate(textField.getText());
454: }
455:
456: public void removeUpdate(DocumentEvent e) {
457: LocationsComboBoxEditor.this .validator
458: .validate(textField.getText());
459: }
460:
461: public void changedUpdate(DocumentEvent e) {
462: LocationsComboBoxEditor.this .validator
463: .validate(textField.getText());
464: }
465: });
466:
467: // l&f tweaks
468: if (!SystemUtils.isMacOS()) {
469: textField.setBorder(new EmptyBorder(1, 1, 1, 1));
470: }
471: }
472:
473: public void setModel(LocationsComboBoxModel model) {
474: this .model = model;
475: }
476:
477: // comboboxeditor ///////////////////////////////////////////////////////////
478: public Component getEditorComponent() {
479: return textField;
480: }
481:
482: public void setItem(Object object) {
483: if (object != null) {
484: final int index = model.getLabels().indexOf(object);
485:
486: if (index != -1) {
487: textField.setText(model.getLocations().get(index));
488: } else {
489: textField.setText(object.toString());
490: }
491: } else {
492: textField.setText("");
493: }
494: }
495:
496: public Object getItem() {
497: final String text = textField.getText();
498: final int index = model.getLocations().indexOf(text);
499:
500: if (index != -1) {
501: return model.getLabels().get(index);
502: } else {
503: return text;
504: }
505: }
506:
507: public void selectAll() {
508: textField.selectAll();
509: }
510:
511: public void addActionListener(ActionListener listener) {
512: textField.addActionListener(listener);
513: }
514:
515: public void removeActionListener(ActionListener listener) {
516: textField.removeActionListener(listener);
517: }
518: }
519:
520: public static class LocationsComboBoxModel implements ComboBoxModel {
521: private List<ListDataListener> listeners;
522:
523: private List<String> locations;
524: private List<String> labels;
525:
526: private String selectedItem;
527: private boolean selectedItemFromList;
528:
529: public LocationsComboBoxModel(List<File> locations,
530: List<String> labels) {
531: this .locations = new LinkedList<String>();
532: for (File file : locations) {
533: this .locations.add(file.toString());
534: }
535:
536: this .labels = new LinkedList<String>();
537: this .labels.addAll(labels);
538:
539: this .listeners = new LinkedList<ListDataListener>();
540:
541: if (labels.size() > 0) {
542: this .selectedItem = labels.get(0);
543: this .selectedItemFromList = true;
544: } else {
545: this .selectedItem = StringUtils.EMPTY_STRING;
546: this .selectedItemFromList = false;
547: }
548: }
549:
550: public List<String> getLabels() {
551: return labels;
552: }
553:
554: public List<String> getLocations() {
555: return locations;
556: }
557:
558: public String getLocation() {
559: if (selectedItemFromList) {
560: return locations.get(labels.indexOf(selectedItem));
561: } else {
562: return selectedItem;
563: }
564: }
565:
566: // comboboxmodel ////////////////////////////////////////////////////////////
567: public void setSelectedItem(Object item) {
568: selectedItem = (String) item;
569:
570: if (labels.indexOf(item) != -1) {
571: selectedItemFromList = true;
572: } else {
573: selectedItemFromList = false;
574: }
575:
576: fireContentsChanged(-1);
577: }
578:
579: public Object getSelectedItem() {
580: return selectedItem;
581: }
582:
583: public int getSize() {
584: return labels.size();
585: }
586:
587: public Object getElementAt(int index) {
588: return labels.get(index);
589: }
590:
591: public void addListDataListener(ListDataListener listener) {
592: synchronized (listeners) {
593: listeners.add(listener);
594: }
595: }
596:
597: public void removeListDataListener(ListDataListener listener) {
598: synchronized (listeners) {
599: listeners.remove(listener);
600: }
601: }
602:
603: // private //////////////////////////////////////////////////////////////////
604: private void fireContentsChanged(int index) {
605: final ListDataListener[] clone;
606: synchronized (listeners) {
607: clone = listeners
608: .toArray(new ListDataListener[listeners.size()]);
609: }
610:
611: final ListDataEvent event = new ListDataEvent(this ,
612: ListDataEvent.CONTENTS_CHANGED, index, index);
613:
614: for (ListDataListener listener : clone) {
615: listener.contentsChanged(event);
616: }
617: }
618: }
619:
620: public static interface LocationValidator {
621: void validate(String location);
622: }
623:
624: /////////////////////////////////////////////////////////////////////////////////
625: // Constants
626: public static final String LOCATION_LABEL_TEXT_PROPERTY = "location.label.text"; // NOI18N
627: public static final String LOCATION_BUTTON_TEXT_PROPERTY = "location.button.text"; // NOI18N
628: public static final String LIST_LABEL_TEXT_PROPERTY = "list.label.text"; // NOI18N
629:
630: public static final String DEFAULT_LOCATION_LABEL_TEXT = ResourceUtils
631: .getString(ApplicationLocationPanel.class,
632: "ALP.location.label.text"); // NOI18N
633: public static final String DEFAULT_LOCATION_BUTTON_TEXT = ResourceUtils
634: .getString(ApplicationLocationPanel.class,
635: "ALP.location.button.text"); // NOI18N
636: public static final String DEFAULT_LIST_LABEL_TEXT = ResourceUtils
637: .getString(ApplicationLocationPanel.class,
638: "ALP.list.label.text"); // NOI18N
639:
640: public static final String ERROR_NOTHING_FOUND_PROPERTY = "error.nothing.found"; // NOI18N
641:
642: public static final String DEFAULT_ERROR_NOTHING_FOUND = ResourceUtils
643: .getString(ApplicationLocationPanel.class,
644: "ALP.error.nothing.found"); // NOI18N
645:
646: public static final String DEFAULT_LOCATION = ResourceUtils
647: .getString(ApplicationLocationPanel.class,
648: "ALP.default.location"); // NOI18N
649: }
|