001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.ui.predictive;
038:
039: import javax.swing.*;
040: import javax.swing.event.*;
041: import javax.swing.text.JTextComponent;
042: import javax.swing.text.Keymap;
043: import java.awt.*;
044: import java.awt.event.*;
045: import java.util.List;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.StringTokenizer;
049: import java.util.NoSuchElementException;
050:
051: import edu.rice.cs.util.Lambda;
052: import edu.rice.cs.util.swing.Utilities;
053:
054: /** Frame with predictive string input based on a list of strings. */
055: public class PredictiveInputFrame<T extends Comparable<? super T>>
056: extends JFrame {
057:
058: /** Interface that is used to generate additional information about an item. */
059: public static interface InfoSupplier<X> extends Lambda<String, X> {
060: public String apply(X param);
061: }
062:
063: // /** General information supplier that just uses toString(). */
064: // public static final InfoSupplier<Object> TO_STRING_SUPPLIER = new InfoSupplier<Object>() {
065: // public String apply(Object param) { return param.toString(); }
066: // };
067:
068: /** Interface for an action to be performed when the user closes the frame,
069: * either by using "OK" or "Cancel".
070: */
071: public static interface CloseAction<X extends Comparable<? super X>>
072: extends Lambda<Object, PredictiveInputFrame<X>> {
073: public Object apply(PredictiveInputFrame<X> param);
074: }
075:
076: /** Class to save the frame state, i.e. location and dimensions.*/
077: public static class FrameState {
078: private volatile Dimension _dim;
079: private volatile Point _loc;
080: private volatile int _currentStrategyIndex;
081:
082: public FrameState(Dimension d, Point l, int currentStrategyIndex) {
083: _dim = d;
084: _loc = l;
085: _currentStrategyIndex = currentStrategyIndex;
086: }
087:
088: public FrameState(String s) {
089: StringTokenizer tok = new StringTokenizer(s);
090: try {
091: int x = Integer.valueOf(tok.nextToken());
092: int y = Integer.valueOf(tok.nextToken());
093: _dim = new Dimension(x, y);
094: x = Integer.valueOf(tok.nextToken());
095: y = Integer.valueOf(tok.nextToken());
096: _loc = new Point(x, y);
097: _currentStrategyIndex = Integer
098: .valueOf(tok.nextToken());
099: } catch (NoSuchElementException nsee) {
100: throw new IllegalArgumentException(
101: "Wrong FrameState string: " + nsee);
102: } catch (NumberFormatException nfe) {
103: throw new IllegalArgumentException(
104: "Wrong FrameState string: " + nfe);
105: }
106: }
107:
108: public FrameState(PredictiveInputFrame comp) {
109: _dim = comp.getSize();
110: _loc = comp.getLocation();
111: _currentStrategyIndex = comp._strategies
112: .indexOf(comp._currentStrategy);
113: }
114:
115: public String toString() {
116: final StringBuilder sb = new StringBuilder();
117: sb.append((int) _dim.getWidth());
118: sb.append(' ');
119: sb.append((int) _dim.getHeight());
120: sb.append(' ');
121: sb.append(_loc.x);
122: sb.append(' ');
123: sb.append(_loc.y);
124: sb.append(' ');
125: sb.append(_currentStrategyIndex);
126: return sb.toString();
127: }
128:
129: public Dimension getDimension() {
130: return _dim;
131: }
132:
133: public Point getLocation() {
134: return _loc;
135: }
136:
137: public int getCurrentStrategyIndex() {
138: return _currentStrategyIndex;
139: }
140: }
141:
142: /** Predictive input model */
143: private volatile PredictiveInputModel<T> _pim;
144:
145: /** Code for the last button that was pressed.*/
146: private volatile int _buttonPressed;
147:
148: /** Ok button.*/
149: private final JButton _okButton = new JButton("OK");
150:
151: /** Text field for string input. */
152: private final JTextField _textField = new JTextField();
153:
154: /** Panel for additional options. */
155: protected JPanel _optionsPanel;
156:
157: /** Optional components for the _optionsPanel. */
158: protected JComponent[] _optionalComponents;
159:
160: /** Label with "Tab completes:" string. */
161: private final JLabel _tabCompletesLabel = new JLabel(
162: "Tab completes: ");
163:
164: /** List with matches. */
165: private final JList _matchList;
166:
167: /** True if the user is forced to select one of the items. */
168: private final boolean _force;
169:
170: /** Label with shared extension.*/
171: private final JLabel _sharedExtLabel = new JLabel("");
172:
173: /** Listener for several events. */
174: private final PredictiveInputListener _listener = new PredictiveInputListener();
175:
176: /** Info supplier. */
177: private final InfoSupplier<? super T> _info;
178:
179: /** Text area for additional information. */
180: private final JLabel _infoLabel = new JLabel("");
181:
182: /** Owner frame. */
183: private final Frame _owner;
184:
185: /** Action to be performed when the user closes the frame using "OK". */
186: private final CloseAction<T> _okAction;
187:
188: /** Action to be performed when the user closes the frame using "Cancel". */
189: private final CloseAction<T> _cancelAction;
190:
191: /** Array of strategies. */
192: private final java.util.List<PredictiveInputModel.MatchingStrategy<T>> _strategies;
193:
194: /** Combo box. */
195: private final JComboBox _strategyBox;
196:
197: /** Last frame state. It can be stored and restored. */
198: private volatile FrameState _lastState;
199:
200: /** Currently used strategy. */
201: private volatile PredictiveInputModel.MatchingStrategy<T> _currentStrategy;
202:
203: /** Create a new predictive string input frame.
204: * @param owner owner frame
205: * @param force true if the user is forced to select one of the items
206: * @param ignoreCase true if case should be ignored
207: * @param info information supplier to use for additional information display
208: * @param strategies array of matching strategies
209: * @param okAction action to be performed when the user closes the frame using "OK"
210: * @param cancelAction action to be performed when the user closes the frame using "Cancel"
211: * @param items list of items
212: */
213:
214: public PredictiveInputFrame(
215: Frame owner,
216: String title,
217: boolean force,
218: boolean ignoreCase,
219: InfoSupplier<? super T> info,
220: java.util.List<PredictiveInputModel.MatchingStrategy<T>> strategies,
221: CloseAction<T> okAction, CloseAction<T> cancelAction,
222: java.util.List<T> items) {
223: super (title);
224: _strategies = strategies;
225: _strategyBox = new JComboBox(_strategies.toArray());
226: _currentStrategy = _strategies.get(0);
227: _pim = new PredictiveInputModel<T>(ignoreCase,
228: _currentStrategy, items);
229: _matchList = new JList(_pim.getMatchingItems().toArray());
230: _force = force;
231: _info = info;
232: _lastState = null;
233: _owner = owner;
234: _okAction = okAction;
235: _cancelAction = cancelAction;
236: init(_info != null);
237: }
238:
239: /** Create a new predictive string input frame.
240: * @param owner owner frame
241: * @param force true if the user is forced to select one of the items
242: * @param info information supplier to use for additional information display
243: * @param strategies array of matching strategies
244: * @param okAction action to be performed when the user closes the frame using "OK"
245: * @param cancelAction action to be performed when the user closes the frame using "Cancel"
246: * @param items varargs/array of items
247: */
248: public PredictiveInputFrame(Frame owner, String title,
249: boolean force, boolean ignoreCase,
250: InfoSupplier<? super T> info,
251: List<PredictiveInputModel.MatchingStrategy<T>> strategies,
252: CloseAction<T> okAction, CloseAction<T> cancelAction,
253: T... items) {
254: this (owner, title, force, ignoreCase, info, strategies,
255: okAction, cancelAction, Arrays.asList(items));
256: // super(title);
257: // _strategies = strategies;
258: // _strategyBox = new JComboBox(_strategies.toArray());
259: // _currentStrategy = _strategies.get(0);
260: // _pim = new PredictiveInputModel<T>(ignoreCase, _currentStrategy, items);
261: // _force = force;
262: // _info = info;
263: // _owner = owner;
264: // _okAction = okAction;
265: // _cancelAction = cancelAction;
266: // init(_info != null);
267: }
268:
269: /** Returns the last state of the frame, i.e. the location and dimension.
270: * @return frame state
271: */
272: public FrameState getFrameState() {
273: return _lastState;
274: }
275:
276: /** Sets state of the frame, i.e. the location and dimension of the frame for the next use.
277: * @param ds State to update to, or {@code null} to reset
278: */
279: public void setFrameState(FrameState ds) {
280: _lastState = ds;
281: if (_lastState != null) {
282: setSize(_lastState.getDimension());
283: setLocation(_lastState.getLocation());
284: int index = _lastState.getCurrentStrategyIndex();
285: if ((index >= 0) && (index < _strategies.size())) {
286: _currentStrategy = _strategies.get(index);
287: _strategyBox.setSelectedIndex(index);
288: }
289: selectStrategy();
290: validate();
291: }
292: }
293:
294: /** Sets state of the frame, i.e. the location and dimension of the frame for the next use.
295: * @param s State to update to, or {@code null} to reset
296: */
297: public void setFrameState(String s) {
298: try {
299: _lastState = new FrameState(s);
300: } catch (IllegalArgumentException e) {
301: _lastState = null;
302: }
303: if (_lastState != null) {
304: setSize(_lastState.getDimension());
305: setLocation(_lastState.getLocation());
306: int index = _lastState.getCurrentStrategyIndex();
307: if ((index >= 0) && (index < _strategies.size())) {
308: _currentStrategy = _strategies.get(index);
309: _strategyBox.setSelectedIndex(index);
310: }
311: selectStrategy();
312: validate();
313: } else {
314: Dimension parentDim = (_owner != null) ? _owner.getSize()
315: : getToolkit().getScreenSize();
316: int xs = (int) parentDim.getWidth() / 3;
317: int ys = (int) parentDim.getHeight() / 4;
318: setSize(Math.max(xs, 400), Math.max(ys, 300));
319: setLocationRelativeTo(_owner);
320: _currentStrategy = _strategies.get(0);
321: _strategyBox.setSelectedIndex(0);
322: selectStrategy();
323: }
324: }
325:
326: /** Set the predictive input model.
327: * @param ignoreCase true if case should be ignored
328: * @param pim predictive input model
329: */
330: public void setModel(boolean ignoreCase, PredictiveInputModel<T> pim) {
331: _pim = new PredictiveInputModel<T>(ignoreCase, pim);
332: removeListener();
333: updateTextField();
334: updateExtensionLabel();
335: updateList();
336: addListener();
337: }
338:
339: /** Set the items.
340: * @param ignoreCase true if case should be ignored
341: * @param items list of items
342: */
343: public void setItems(boolean ignoreCase, List<T> items) {
344: _pim = new PredictiveInputModel<T>(ignoreCase,
345: _currentStrategy, items);
346: removeListener();
347: updateTextField();
348: updateExtensionLabel();
349: updateList();
350: addListener();
351: }
352:
353: /** Set the currently selected item.
354: * @param item item to select
355: */
356: public void setCurrentItem(T item) {
357: _pim.setCurrentItem(item);
358: removeListener();
359: updateTextField();
360: updateExtensionLabel();
361: updateList();
362: addListener();
363: }
364:
365: /** Set the items.
366: * @param ignoreCase true if case should be ignored
367: * @param items varargs/array of items
368: */
369: public void setItems(boolean ignoreCase, T... items) {
370: _pim = new PredictiveInputModel<T>(ignoreCase,
371: _currentStrategy, items);
372: removeListener();
373: updateTextField();
374: updateExtensionLabel();
375: updateList();
376: addListener();
377: }
378:
379: /** Return the code for the last button that was pressed. This will be either JOptionPane.OK_OPTION or
380: * JOptionPane.CANCEL_OPTION.
381: * @return button code
382: */
383: public int getButtonPressed() {
384: return _buttonPressed;
385: }
386:
387: /** Return the string that was entered in the text field.
388: * If the user is forced to select an item, then the text of the item will be returned.
389: * @return text in text field
390: */
391: public String getText() {
392: if (_force) {
393: @SuppressWarnings("unchecked")
394: T item = (T) _matchList.getSelectedValue();
395: return (item == null) ? "" : _currentStrategy.force(item,
396: _textField.getText());
397: }
398: return _textField.getText();
399: }
400:
401: /** Return the item that was selected or null the user entered a mask not in the list and force == false.
402: * @return item that was selected or null
403: */
404: public T getItem() {
405: if (!_force && _pim.getMatchingItems().size() == 0)
406: return null;
407: @SuppressWarnings("unchecked")
408: T item = (T) _matchList.getSelectedValue();
409: return item;
410: }
411:
412: /** Initialize the frame.
413: * @param info true if additional information is desired
414: */
415: private void init(boolean info) {
416: _buttonPressed = JOptionPane.CANCEL_OPTION;
417: addWindowListener(new java.awt.event.WindowAdapter() {
418: public void windowClosing(WindowEvent winEvt) {
419: cancelButtonPressed();
420: }
421: });
422: addComponentListener(new java.awt.event.ComponentAdapter() {
423: public void componentResized(ComponentEvent e) {
424: validate();
425: _matchList.ensureIndexIsVisible(_matchList
426: .getSelectedIndex());
427: }
428: });
429:
430: // buttons
431: _okButton.addActionListener(new ActionListener() {
432: public void actionPerformed(ActionEvent e) {
433: okButtonPressed();
434: }
435: });
436:
437: getRootPane().setDefaultButton(_okButton);
438:
439: final JButton cancelButton = new JButton("Cancel");
440: cancelButton.addActionListener(new ActionListener() {
441: public void actionPerformed(ActionEvent e) {
442: cancelButtonPressed();
443: }
444: });
445:
446: _strategyBox.setEditable(false);
447: _strategyBox.addActionListener(new ActionListener() {
448: public void actionPerformed(ActionEvent e) {
449: // System.out.println("set strategy!");
450: selectStrategy();
451: }
452: });
453: _strategyBox.addFocusListener(new FocusAdapter() {
454:
455: public void focusLost(FocusEvent e) {
456: if ((e.getOppositeComponent() != _textField)
457: && (e.getOppositeComponent() != _okButton)
458: && (e.getOppositeComponent() != cancelButton)) {
459: for (JComponent c : _optionalComponents) {
460: if (e.getOppositeComponent() == c) {
461: return;
462: }
463: }
464: _textField.requestFocus();
465: }
466: }
467: });
468:
469: // text field
470: _textField.setDragEnabled(false);
471: _textField.setFocusTraversalKeysEnabled(false);
472:
473: addListener();
474:
475: Keymap ourMap = JTextComponent.addKeymap(
476: "PredictiveInputFrame._textField", _textField
477: .getKeymap());
478: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
479: KeyEvent.VK_ESCAPE, 0), new AbstractAction() {
480: public void actionPerformed(ActionEvent e) {
481: // System.out.println("esc!");
482: cancelButtonPressed();
483: }
484: });
485: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
486: KeyEvent.VK_ENTER, 0), new AbstractAction() {
487: public void actionPerformed(ActionEvent e) {
488: // System.out.println("enter!");
489: okButtonPressed();
490: }
491: });
492: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
493: KeyEvent.VK_TAB, 0), new AbstractAction() {
494: public void actionPerformed(ActionEvent e) {
495: // System.out.println("tab!");
496: removeListener();
497: _pim.extendSharedMask();
498: updateTextField();
499: updateExtensionLabel();
500: updateList();
501: addListener();
502: }
503: });
504: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
505: KeyEvent.VK_UP, 0), new AbstractAction() {
506: public void actionPerformed(ActionEvent e) {
507: // System.out.println("up!");
508: if (_matchList.getModel().getSize() > 0) {
509: removeListener();
510: int i = _matchList.getSelectedIndex();
511: if (i > 0) {
512: _matchList.setSelectedIndex(i - 1);
513: _matchList.ensureIndexIsVisible(i - 1);
514: _pim.setCurrentItem(_pim.getMatchingItems()
515: .get(i - 1));
516: updateInfo();
517: }
518: addListener();
519: }
520: }
521: });
522: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
523: KeyEvent.VK_DOWN, 0), new AbstractAction() {
524: public void actionPerformed(ActionEvent e) {
525: // System.out.println("down!");
526: if (_matchList.getModel().getSize() > 0) {
527: removeListener();
528: int i = _matchList.getSelectedIndex();
529: if (i < _matchList.getModel().getSize() - 1) {
530: _matchList.setSelectedIndex(i + 1);
531: _matchList.ensureIndexIsVisible(i + 1);
532: _pim.setCurrentItem(_pim.getMatchingItems()
533: .get(i + 1));
534: updateInfo();
535: }
536: addListener();
537: }
538: }
539: });
540: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
541: KeyEvent.VK_PAGE_UP, 0), new AbstractAction() {
542: public void actionPerformed(ActionEvent e) {
543: // System.out.println("page up!");
544: if (_matchList.getModel().getSize() > 0) {
545: removeListener();
546: int page = _matchList.getLastVisibleIndex()
547: - _matchList.getFirstVisibleIndex() + 1;
548: int i = _matchList.getSelectedIndex() - page;
549: if (i < 0)
550: i = 0;
551: _matchList.setSelectedIndex(i);
552: _matchList.ensureIndexIsVisible(i);
553: _pim.setCurrentItem(_pim.getMatchingItems().get(i));
554: updateInfo();
555: addListener();
556: }
557: }
558: });
559: ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(
560: KeyEvent.VK_PAGE_DOWN, 0), new AbstractAction() {
561: public void actionPerformed(ActionEvent e) {
562: // System.out.println("page down!");
563: if (_matchList.getModel().getSize() > 0) {
564: removeListener();
565: int page = _matchList.getLastVisibleIndex()
566: - _matchList.getFirstVisibleIndex() + 1;
567: int i = _matchList.getSelectedIndex() + page;
568: if (i >= _matchList.getModel().getSize()) {
569: i = _matchList.getModel().getSize() - 1;
570: }
571: _matchList.setSelectedIndex(i);
572: _matchList.ensureIndexIsVisible(i);
573: _pim.setCurrentItem(_pim.getMatchingItems().get(i));
574: updateInfo();
575: addListener();
576: }
577: }
578: });
579: _textField.setKeymap(ourMap);
580:
581: // _textField.addKeyListener(new KeyAdapter() {
582: // public void keyTyped(KeyEvent e) {
583: // char c = e.getKeyChar();
584: // if ((c != KeyEvent.VK_DELETE) && (c != KeyEvent.VK_BACK_SPACE) && (c >= 32)) {
585: // String oldMask = _pim.getMask();
586: // String newMask = oldMask.substring(0, _textField.getCaretPosition()) + c +
587: // oldMask.substring(_textField.getCaretPosition());
588: // _pim.setMask(newMask);
589: // if (_force && (_pim.getMatchingItems().size()==0)) {
590: // Toolkit.getDefaultToolkit().beep();
591: // e.consume();
592: // }
593: // _pim.setMask(oldMask);
594: // }
595: // }
596: // });
597:
598: _textField.addFocusListener(new FocusAdapter() {
599: public void focusLost(FocusEvent e) {
600: if ((e.getOppositeComponent() != _strategyBox)
601: && (e.getOppositeComponent() != _okButton)
602: && (e.getOppositeComponent() != cancelButton)) {
603: for (JComponent c : _optionalComponents) {
604: if (e.getOppositeComponent() == c) {
605: return;
606: }
607: }
608: _textField.requestFocus();
609: }
610: }
611: });
612:
613: _matchList
614: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
615: _matchList
616: .addListSelectionListener(new ListSelectionListener() {
617: public void valueChanged(ListSelectionEvent e) {
618: // System.out.println("click!");
619: removeListener();
620: int i = _matchList.getSelectedIndex();
621: if (i >= 0) {
622: _pim.setCurrentItem(_pim.getMatchingItems()
623: .get(i));
624: _matchList.ensureIndexIsVisible(i);
625: updateInfo();
626: }
627: addListener();
628: }
629: });
630:
631: // put everything together
632: Container contentPane = getContentPane();
633:
634: GridBagLayout layout = new GridBagLayout();
635: contentPane.setLayout(layout);
636:
637: GridBagConstraints c = new GridBagConstraints();
638: c.anchor = GridBagConstraints.NORTHWEST;
639: c.weightx = 1.0;
640: c.weighty = 0.0;
641: c.gridwidth = GridBagConstraints.REMAINDER; // end row
642: c.insets.top = 2;
643: c.insets.left = 2;
644: c.insets.bottom = 2;
645: c.insets.right = 2;
646:
647: if (info) {
648: c.fill = GridBagConstraints.NONE;
649: contentPane.add(_infoLabel, c);
650: }
651:
652: c.fill = GridBagConstraints.BOTH;
653: c.weighty = 1.0;
654: contentPane.add(new JScrollPane(_matchList,
655: JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
656: JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), c);
657:
658: c.anchor = GridBagConstraints.SOUTHWEST;
659: c.fill = GridBagConstraints.NONE;
660: c.weightx = 0.0;
661: c.weighty = 0.0;
662: c.gridwidth = 1;
663: contentPane.add(_tabCompletesLabel, c);
664:
665: c.fill = GridBagConstraints.HORIZONTAL;
666: c.weightx = 1.0;
667: c.gridwidth = GridBagConstraints.REMAINDER;
668: contentPane.add(_sharedExtLabel, c);
669:
670: contentPane.add(_textField, c);
671:
672: _optionalComponents = makeOptions();
673: if (_optionalComponents.length > 0) {
674: _optionsPanel = new JPanel(new BorderLayout());
675: _setupOptionsPanel(_optionalComponents);
676: contentPane.add(_optionsPanel, c);
677: }
678:
679: c.anchor = GridBagConstraints.SOUTH;
680:
681: JPanel buttonPanel = new JPanel(new GridBagLayout());
682: GridBagConstraints bc = new GridBagConstraints();
683: bc.insets.left = 2;
684: bc.insets.right = 2;
685: buttonPanel.add(new JLabel("Matching strategy:"), bc);
686: buttonPanel.add(_strategyBox, bc);
687: buttonPanel.add(_okButton, bc);
688: buttonPanel.add(cancelButton, bc);
689:
690: contentPane.add(buttonPanel, c);
691:
692: Dimension parentDim = (_owner != null) ? _owner.getSize()
693: : getToolkit().getScreenSize();
694: int xs = (int) parentDim.getWidth() / 3;
695: int ys = (int) parentDim.getHeight() / 4;
696: setSize(Math.max(xs, 400), Math.max(ys, 300));
697: setLocationRelativeTo(_owner);
698:
699: removeListener();
700: updateTextField();
701: addListener();
702: updateList();
703: }
704:
705: /** Creates the optional components. Should be overridden. */
706: protected JComponent[] makeOptions() {
707: return new JComponent[0];
708: }
709:
710: /** Creates the panel with the optional components. */
711: private void _setupOptionsPanel(JComponent[] components) {
712: JPanel mainButtons = new JPanel();
713: JPanel emptyPanel = new JPanel();
714: GridBagLayout gbLayout = new GridBagLayout();
715: GridBagConstraints c = new GridBagConstraints();
716: mainButtons.setLayout(gbLayout);
717:
718: for (JComponent b : components) {
719: mainButtons.add(b);
720: }
721: mainButtons.add(emptyPanel);
722:
723: c.fill = GridBagConstraints.HORIZONTAL;
724: c.anchor = GridBagConstraints.NORTH;
725: c.gridwidth = GridBagConstraints.REMAINDER;
726: c.weightx = 1.0;
727:
728: for (JComponent b : components) {
729: gbLayout.setConstraints(b, c);
730: }
731:
732: c.fill = GridBagConstraints.BOTH;
733: c.anchor = GridBagConstraints.SOUTH;
734: c.gridheight = GridBagConstraints.REMAINDER;
735: c.weighty = 1.0;
736:
737: gbLayout.setConstraints(emptyPanel, c);
738:
739: _optionsPanel.add(mainButtons, BorderLayout.CENTER);
740: }
741:
742: /**
743: * Enable or disable owner. Can be overridden to toggle the hourglass, etc.
744: * @param b whether the owner should be enabled (true) or disabled
745: */
746: public void setOwnerEnabled(boolean b) {
747: // do nothing by default
748: }
749:
750: /** Validates before changing visibility,
751: * @param b true if frame should be shown, false if it should be hidden.
752: */
753: public void setVisible(boolean b) {
754: validate();
755: super .setVisible(b);
756: if (b) {
757: setOwnerEnabled(false);
758: _textField.requestFocus();
759: } else {
760: setOwnerEnabled(true);
761: _owner.toFront();
762: }
763: }
764:
765: /** Add the listener. */
766: private void addListener() {
767: _textField.getDocument().addDocumentListener(_listener);
768: _textField.addCaretListener(_listener);
769: }
770:
771: /** Remove the listener. */
772: private void removeListener() {
773: _textField.getDocument().removeDocumentListener(_listener);
774: _textField.removeCaretListener(_listener);
775: }
776:
777: /** Update the text field based on the model. */
778: private void updateTextField() {
779: _textField.setText(_pim.getMask());
780: _textField.setCaretPosition(_pim.getMask().length());
781: }
782:
783: /** Focus back in the text field. */
784: public void resetFocus() {
785: _textField.requestFocus();
786: }
787:
788: /** Update the extension label based on the model. */
789: private void updateExtensionLabel() {
790: _sharedExtLabel.setText(_pim.getSharedMaskExtension() + " ");
791: _tabCompletesLabel.setVisible(_pim.getSharedMaskExtension()
792: .length() > 0);
793: }
794:
795: /** Update the match list based on the model. */
796: private void updateList() {
797: _matchList.setListData(_pim.getMatchingItems().toArray());
798: _matchList.setSelectedValue(_pim.getCurrentItem(), true);
799: updateExtensionLabel();
800: updateInfo();
801: if (_force) {
802: _okButton.setEnabled(_matchList.getModel().getSize() > 0);
803: }
804: }
805:
806: /** Update the information. */
807: private void updateInfo() {
808: if (_info == null)
809: return;
810: if (_matchList.getModel().getSize() > 0) {
811: @SuppressWarnings("unchecked")
812: T item = (T) _matchList.getSelectedValue();
813: _infoLabel.setText("Path: " + _info.apply(item));
814: } else
815: _infoLabel.setText("No file selected");
816: }
817:
818: /** Handle OK button. */
819: private void okButtonPressed() {
820: if (_force && (_matchList.getModel().getSize() == 0)) {
821: Toolkit.getDefaultToolkit().beep();
822: } else {
823: _buttonPressed = JOptionPane.OK_OPTION;
824: _lastState = new FrameState(PredictiveInputFrame.this );
825: setVisible(false);
826: _okAction.apply(this );
827: }
828: }
829:
830: /** Handle cancel button. */
831: private void cancelButtonPressed() {
832: _buttonPressed = JOptionPane.CANCEL_OPTION;
833: _lastState = new FrameState(PredictiveInputFrame.this );
834: setVisible(false);
835: _cancelAction.apply(this );
836: }
837:
838: /** Select the strategy for matching. */
839: public void selectStrategy() {
840: _currentStrategy = _strategies.get(_strategyBox
841: .getSelectedIndex());
842: removeListener();
843: _pim.setStrategy(_currentStrategy);
844: updateTextField();
845: updateExtensionLabel();
846: updateList();
847: addListener();
848: _textField.requestFocus();
849: }
850:
851: /** Listener for several events. */
852: private class PredictiveInputListener implements CaretListener,
853: DocumentListener {
854: public void insertUpdate(DocumentEvent e) {
855: // System.out.println("insertUpdate fired!");
856: Utilities.invokeLater(new Runnable() {
857: public void run() {
858: removeListener();
859: _pim.setMask(_textField.getText());
860: updateExtensionLabel();
861: updateList();
862: addListener();
863: }
864: });
865: }
866:
867: public void removeUpdate(DocumentEvent e) {
868: // System.out.println("removeUpdate fired!");
869: Utilities.invokeLater(new Runnable() {
870: public void run() {
871: removeListener();
872: _pim.setMask(_textField.getText());
873: updateExtensionLabel();
874: updateList();
875: addListener();
876: }
877: });
878: }
879:
880: public void changedUpdate(DocumentEvent e) {
881: // System.out.println("changedUpdate fired!");
882: Utilities.invokeLater(new Runnable() {
883: public void run() {
884: removeListener();
885: _pim.setMask(_textField.getText());
886: updateExtensionLabel();
887: updateList();
888: addListener();
889: }
890: });
891: }
892:
893: public void caretUpdate(CaretEvent e) {
894: }
895: }
896: }
|