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-2007 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.visualweb.propertyeditors;
042:
043: import java.awt.Font;
044: import java.awt.event.KeyEvent;
045: import java.awt.event.KeyListener;
046: import java.util.Arrays;
047: import java.util.HashSet;
048: import java.util.Set;
049: import java.util.Timer;
050: import java.util.TimerTask;
051: import javax.swing.DefaultListModel;
052: import javax.swing.JList;
053: import javax.swing.JPanel;
054: import javax.swing.ListModel;
055:
056: /**
057: * A custom property editor for selecting CSS style classes from among a list of
058: * all available classes.
059: *
060: * @author gjmurphy
061: */
062: public class StyleClassPropertyPanel extends PropertyPanelBase {
063:
064: static final String SELECT_ACTION = "select"; //NOI18N
065: static final String DESELECT_ACTION = "deselect"; //NOI18N
066: static final String SELECT_ALL_ACTION = "select_all"; //NOI18N
067: static final String DESELECT_ALL_ACTION = "deselect_all"; //NOI18N
068:
069: StyleClassPropertyEditor editor;
070:
071: DefaultListModel availableListModel;
072: DefaultListModel selectedListModel;
073:
074: public StyleClassPropertyPanel(StyleClassPropertyEditor editor) {
075: super (editor);
076: assert (editor != null);
077: this .editor = editor;
078: // Initialize list models
079: this .selectedListModel = new DefaultListModel();
080: String[] selectedStyleClasses = editor.getStyleClasses();
081: Set selectStyleClassesSet = new HashSet();
082: for (int i = 0; i < selectedStyleClasses.length; i++) {
083: this .selectedListModel.addElement(selectedStyleClasses[i]);
084: selectStyleClassesSet.add(selectedStyleClasses[i]);
085: }
086: this .availableListModel = new DefaultListModel();
087: String[] availableStyleClasses = editor
088: .getAvailableStyleClasses();
089: for (int i = 0; i < availableStyleClasses.length; i++) {
090: if (!selectStyleClassesSet
091: .contains(availableStyleClasses[i]))
092: this .availableListModel
093: .addElement(availableStyleClasses[i]);
094: }
095: // Initialize UI components
096: initComponents();
097: this .availableList.addKeyListener(new ListKeyListener(
098: this .availableList));
099: }
100:
101: public Object getPropertyValue() {
102: if (this .selectedListModel.size() == 0)
103: return null;
104: StringBuffer buffer = new StringBuffer();
105: buffer.append(this .selectedListModel.get(0));
106: for (int i = 1; i < this .selectedListModel.size(); i++) {
107: buffer.append(" ");
108: buffer.append(this .selectedListModel.get(i));
109: }
110: return buffer.toString();
111: }
112:
113: /** This method is called from within the constructor to
114: * initialize the form.
115: * WARNING: Do NOT modify this code. The content of this method is
116: * always regenerated by the Form Editor.
117: */
118: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
119: private void initComponents() {
120: java.awt.GridBagConstraints gridBagConstraints;
121:
122: titleLabel = new javax.swing.JLabel();
123: availableListScrollPane = new javax.swing.JScrollPane();
124: availableList = new JList(this .availableListModel);
125: selectButtonPanel = new javax.swing.JPanel();
126: selectButton = new javax.swing.JButton();
127: deselectButton = new javax.swing.JButton();
128: selectAllButton = new javax.swing.JButton();
129: deselectAllButton = new javax.swing.JButton();
130: selectedListScrollPane = new javax.swing.JScrollPane();
131: selectedList = new JList(this .selectedListModel);
132: availableLabel = new javax.swing.JLabel();
133: selectedLabel = new javax.swing.JLabel();
134:
135: setLayout(new java.awt.GridBagLayout());
136:
137: java.util.ResourceBundle bundle = java.util.ResourceBundle
138: .getBundle("org/netbeans/modules/visualweb/propertyeditors/Bundle"); // NOI18N
139: titleLabel.setText(bundle
140: .getString("StyleClassPropertyEditor.title")); // NOI18N
141: gridBagConstraints = new java.awt.GridBagConstraints();
142: gridBagConstraints.gridx = 0;
143: gridBagConstraints.gridy = 0;
144: gridBagConstraints.gridwidth = 3;
145: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
146: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
147: gridBagConstraints.insets = new java.awt.Insets(0, 11, 6, 11);
148: add(titleLabel, gridBagConstraints);
149:
150: availableListScrollPane.setViewportView(availableList);
151: availableList
152: .getAccessibleContext()
153: .setAccessibleName(
154: bundle
155: .getString("StyleClassPropertyEditor.availableList.AccessibleName")); // NOI18N
156: availableList
157: .getAccessibleContext()
158: .setAccessibleDescription(
159: bundle
160: .getString("StyleClassPropertyEditor.availableList.AccessibleDescription")); // NOI18N
161:
162: gridBagConstraints = new java.awt.GridBagConstraints();
163: gridBagConstraints.gridx = 0;
164: gridBagConstraints.gridy = 2;
165: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
166: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
167: gridBagConstraints.weightx = 1.0;
168: gridBagConstraints.weighty = 1.0;
169: gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 0);
170: add(availableListScrollPane, gridBagConstraints);
171:
172: selectButtonPanel.setLayout(new javax.swing.BoxLayout(
173: selectButtonPanel, javax.swing.BoxLayout.Y_AXIS));
174:
175: selectButton.setFont(selectButton.getFont().deriveFont(
176: Font.BOLD));
177: selectButton.setText(">");
178: selectButton.setActionCommand(SELECT_ACTION);
179: selectButton.setAlignmentY(0.0F);
180: // selectButton.setMaximumSize(new java.awt.Dimension(45, 23));
181: selectButton.setMinimumSize(new java.awt.Dimension(45, 23));
182: selectButton
183: .addActionListener(new java.awt.event.ActionListener() {
184: public void actionPerformed(
185: java.awt.event.ActionEvent evt) {
186: handleButtonAction(evt);
187: }
188: });
189: selectButtonPanel.add(selectButton);
190: selectButton
191: .getAccessibleContext()
192: .setAccessibleDescription(
193: bundle
194: .getString("StyleClassPropertyEditor.addButton.AccessibleDescription")); // NOI18N
195:
196: deselectButton.setFont(deselectButton.getFont().deriveFont(
197: Font.BOLD));
198: deselectButton.setText("<");
199: deselectButton.setActionCommand(DESELECT_ACTION);
200: // deselectButton.setMaximumSize(new java.awt.Dimension(45, 23));
201: deselectButton.setMinimumSize(new java.awt.Dimension(45, 23));
202: selectButtonPanel.add(javax.swing.Box
203: .createRigidArea(new java.awt.Dimension(0, 5)));
204: deselectButton
205: .addActionListener(new java.awt.event.ActionListener() {
206: public void actionPerformed(
207: java.awt.event.ActionEvent evt) {
208: handleButtonAction(evt);
209: }
210: });
211: selectButtonPanel.add(deselectButton);
212: deselectButton
213: .getAccessibleContext()
214: .setAccessibleDescription(
215: bundle
216: .getString("StyleClassPropertyEditor.removeButton.AccessibleDescription")); // NOI18N
217:
218: selectAllButton.setFont(selectAllButton.getFont().deriveFont(
219: Font.BOLD));
220: selectAllButton.setText(">>");
221: selectAllButton.setActionCommand(SELECT_ALL_ACTION);
222: selectButtonPanel.add(javax.swing.Box
223: .createRigidArea(new java.awt.Dimension(0, 5)));
224: selectAllButton
225: .addActionListener(new java.awt.event.ActionListener() {
226: public void actionPerformed(
227: java.awt.event.ActionEvent evt) {
228: handleButtonAction(evt);
229: }
230: });
231: selectButtonPanel.add(selectAllButton);
232: selectAllButton
233: .getAccessibleContext()
234: .setAccessibleDescription(
235: bundle
236: .getString("StyleClassPropertyEditor.addAllButton.AccessibleDescription")); // NOI18N
237:
238: deselectAllButton.setFont(deselectAllButton.getFont()
239: .deriveFont(Font.BOLD));
240: deselectAllButton.setText("<<");
241: deselectAllButton.setActionCommand(DESELECT_ALL_ACTION);
242: selectButtonPanel.add(javax.swing.Box
243: .createRigidArea(new java.awt.Dimension(0, 5)));
244: deselectAllButton
245: .addActionListener(new java.awt.event.ActionListener() {
246: public void actionPerformed(
247: java.awt.event.ActionEvent evt) {
248: handleButtonAction(evt);
249: }
250: });
251: selectButtonPanel.add(deselectAllButton);
252: deselectAllButton
253: .getAccessibleContext()
254: .setAccessibleDescription(
255: bundle
256: .getString("StyleClassPropertyEditor.removeAllButton.AccessibleDescription")); // NOI18N
257:
258: gridBagConstraints = new java.awt.GridBagConstraints();
259: gridBagConstraints.gridx = 1;
260: gridBagConstraints.gridy = 2;
261: gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 6);
262: add(selectButtonPanel, gridBagConstraints);
263:
264: selectedListScrollPane.setViewportView(selectedList);
265: selectedList
266: .getAccessibleContext()
267: .setAccessibleName(
268: bundle
269: .getString("StyleClassPropertyEditor.selectedList.AccessibleName")); // NOI18N
270: selectedList
271: .getAccessibleContext()
272: .setAccessibleDescription(
273: bundle
274: .getString("StyleClassPropertyEditor.selectedList.AccessibleDescription")); // NOI18N
275:
276: gridBagConstraints = new java.awt.GridBagConstraints();
277: gridBagConstraints.gridx = 2;
278: gridBagConstraints.gridy = 2;
279: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
280: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
281: gridBagConstraints.weightx = 1.0;
282: gridBagConstraints.weighty = 1.0;
283: gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9);
284: add(selectedListScrollPane, gridBagConstraints);
285:
286: availableLabel
287: .setDisplayedMnemonic(java.util.ResourceBundle
288: .getBundle(
289: "org/netbeans/modules/visualweb/propertyeditors/Bundle")
290: .getString(
291: "StyleClassPropertyEditor.availableStyleClasses.mnemonic")
292: .charAt(0));
293: availableLabel.setLabelFor(availableList);
294: availableLabel
295: .setText(bundle
296: .getString("StyleClassPropertyEditor.availableStyleClasses")); // NOI18N
297: gridBagConstraints = new java.awt.GridBagConstraints();
298: gridBagConstraints.gridx = 0;
299: gridBagConstraints.gridy = 1;
300: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
301: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
302: gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 0);
303: add(availableLabel, gridBagConstraints);
304:
305: selectedLabel
306: .setDisplayedMnemonic(java.util.ResourceBundle
307: .getBundle(
308: "org/netbeans/modules/visualweb/propertyeditors/Bundle")
309: .getString(
310: "StyleClassPropertyEditor.selectedStyleClasses.mnemonic")
311: .charAt(0));
312: selectedLabel.setLabelFor(selectedList);
313: selectedLabel
314: .setText(bundle
315: .getString("StyleClassPropertyEditor.selectedStyleClasses")); // NOI18N
316: gridBagConstraints = new java.awt.GridBagConstraints();
317: gridBagConstraints.gridx = 2;
318: gridBagConstraints.gridy = 1;
319: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
320: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
321: gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
322: add(selectedLabel, gridBagConstraints);
323: }// </editor-fold>//GEN-END:initComponents
324:
325: private void handleButtonAction(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_handleButtonAction
326: String action = evt.getActionCommand();
327: if (action.equals(SELECT_ACTION)) {
328: int[] selectedIndices = this .availableList
329: .getSelectedIndices();
330: for (int i = selectedIndices.length - 1; i >= 0; i--)
331: this .selectedListModel
332: .addElement(this .availableListModel
333: .remove(selectedIndices[i]));
334: } else if (action.equals(DESELECT_ACTION)) {
335: int[] selectedIndices = this .selectedList
336: .getSelectedIndices();
337: for (int i = selectedIndices.length - 1; i >= 0; i--) {
338: String styleClass = (String) this .selectedListModel
339: .remove(selectedIndices[i]);
340: this .availableListModel.add(-1
341: - Arrays.binarySearch(this .availableListModel
342: .toArray(), styleClass), styleClass);
343: }
344: } else if (action.equals(SELECT_ALL_ACTION)) {
345: for (int i = 0; i < this .availableListModel.size(); i++)
346: this .selectedListModel
347: .addElement(this .availableListModel.get(i));
348: this .availableListModel.removeAllElements();
349: } else if (action.equals(DESELECT_ALL_ACTION)) {
350: for (int i = 0; i < this .selectedListModel.size(); i++) {
351: String styleClass = (String) this .selectedListModel
352: .get(i);
353: this .availableListModel.add(-1
354: - Arrays.binarySearch(this .availableListModel
355: .toArray(), styleClass), styleClass);
356: }
357: this .selectedListModel.removeAllElements();
358: }
359: }//GEN-LAST:event_handleButtonAction
360:
361: // Variables declaration - do not modify//GEN-BEGIN:variables
362: private javax.swing.JLabel availableLabel;
363: private javax.swing.JList availableList;
364: private javax.swing.JScrollPane availableListScrollPane;
365: private javax.swing.JButton deselectAllButton;
366: private javax.swing.JButton deselectButton;
367: private javax.swing.JButton selectAllButton;
368: private javax.swing.JButton selectButton;
369: private javax.swing.JPanel selectButtonPanel;
370: private javax.swing.JLabel selectedLabel;
371: private javax.swing.JList selectedList;
372: private javax.swing.JScrollPane selectedListScrollPane;
373: private javax.swing.JLabel titleLabel;
374:
375: // End of variables declaration//GEN-END:variables
376:
377: /**
378: * A listener that implements key-based navigation through a JList.
379: */
380: class ListKeyListener implements KeyListener {
381:
382: // Delay in milliseconds before keystroke buffer will be erased
383: static final long BUFFER_ERASE_DELAY = 350;
384:
385: JList list;
386: StringBuffer keyStrokeBuffer;
387: Timer keyStrokeTimer;
388: TimerTask bufferEraseTask;
389:
390: ListKeyListener(JList list) {
391: this .list = list;
392: keyStrokeBuffer = new StringBuffer(16);
393: keyStrokeTimer = new Timer();
394: }
395:
396: public void keyTyped(KeyEvent event) {
397: char c = event.getKeyChar();
398: if (!Character.isISOControl(c)) {
399: synchronized (keyStrokeBuffer) {
400: if (bufferEraseTask != null)
401: bufferEraseTask.cancel();
402: keyStrokeBuffer.append(c);
403: bufferEraseTask = new TimerTask() {
404: public void run() {
405: keyStrokeBuffer.setLength(0);
406: }
407: };
408: repositionSelectedItem(keyStrokeBuffer.toString());
409: keyStrokeTimer.schedule(bufferEraseTask,
410: BUFFER_ERASE_DELAY);
411: }
412: }
413: }
414:
415: private void repositionSelectedItem(String prefix) {
416: int i = this .list.getSelectedIndex() - 1;
417: ListModel listModel = this .list.getModel();
418: // If an item is already selected, try to find the next item that
419: // matches the current set of typed keys
420: if (i >= 0) {
421: for (int j = i + 1; j < listModel.getSize(); j++) {
422: if (((String) listModel.getElementAt(j))
423: .regionMatches(true, 0, prefix, 0, prefix
424: .length())) {
425: this .list.setSelectedIndex(j);
426: this .list.ensureIndexIsVisible(j);
427: return;
428: }
429: }
430: }
431: // Either no item was selected, or an item was selected but no
432: // subsequent matching item was found: so, start search for a match
433: // from the beginning of the list.
434: for (int j = 0; j < listModel.getSize(); j++) {
435: if (((String) listModel.getElementAt(j)).regionMatches(
436: true, 0, prefix, 0, prefix.length())) {
437: this .list.setSelectedIndex(j);
438: this .list.ensureIndexIsVisible(j);
439: return;
440: }
441: }
442: // No matches were found anywhere in the list: leave current selection,
443: // if any, alone.
444: return;
445: }
446:
447: public void keyReleased(KeyEvent event) {
448: }
449:
450: public void keyPressed(KeyEvent event) {
451: }
452:
453: }
454: }
|