1 33 34 package edu.rice.cs.drjava.ui.predictive; 35 36 import javax.swing.*; 37 import javax.swing.event.*; 38 import javax.swing.text.JTextComponent ; 39 import javax.swing.text.Keymap ; 40 import java.awt.*; 41 import java.awt.event.*; 42 import java.util.List ; 43 import java.util.ArrayList ; 44 import java.util.Arrays ; 45 import java.util.StringTokenizer ; 46 import java.util.NoSuchElementException ; 47 48 import edu.rice.cs.util.Lambda; 49 import edu.rice.cs.util.swing.Utilities; 50 51 52 public class PredictiveInputFrame<T extends Comparable <? super T>> extends JFrame { 53 54 55 public static interface InfoSupplier<X> extends Lambda<String , X> { 56 public String apply(X param); 57 } 58 59 64 67 public static interface CloseAction<X extends Comparable <? super X>> extends Lambda<Object , PredictiveInputFrame<X>> { 68 public Object apply(PredictiveInputFrame<X> param); 69 } 70 71 72 public static class FrameState { 73 private volatile Dimension _dim; 74 private volatile Point _loc; 75 private volatile int _currentStrategyIndex; 76 public FrameState(Dimension d, Point l, int currentStrategyIndex) { 77 _dim = d; 78 _loc = l; 79 _currentStrategyIndex = currentStrategyIndex; 80 } 81 public FrameState(String s) { 82 StringTokenizer tok = new StringTokenizer (s); 83 try { 84 int x = Integer.valueOf(tok.nextToken()); 85 int y = Integer.valueOf(tok.nextToken()); 86 _dim = new Dimension(x, y); 87 x = Integer.valueOf(tok.nextToken()); 88 y = Integer.valueOf(tok.nextToken()); 89 _loc = new Point(x, y); 90 _currentStrategyIndex = Integer.valueOf(tok.nextToken()); 91 } 92 catch(NoSuchElementException nsee) { 93 throw new IllegalArgumentException ("Wrong FrameState string: " + nsee); 94 } 95 catch(NumberFormatException nfe) { 96 throw new IllegalArgumentException ("Wrong FrameState string: " + nfe); 97 } 98 } 99 public FrameState(PredictiveInputFrame comp) { 100 _dim = comp.getSize(); 101 _loc = comp.getLocation(); 102 _currentStrategyIndex = comp._strategies.indexOf(comp._currentStrategy); 103 } 104 public String toString() { 105 final StringBuilder sb = new StringBuilder (); 106 sb.append((int)_dim.getWidth()); 107 sb.append(' '); 108 sb.append((int)_dim.getHeight()); 109 sb.append(' '); 110 sb.append(_loc.x); 111 sb.append(' '); 112 sb.append(_loc.y); 113 sb.append(' '); 114 sb.append(_currentStrategyIndex); 115 return sb.toString(); 116 } 117 public Dimension getDimension() { return _dim; } 118 public Point getLocation() { return _loc; } 119 public int getCurrentStrategyIndex() { return _currentStrategyIndex; } 120 } 121 122 123 private volatile PredictiveInputModel<T> _pim; 124 125 126 private volatile int _buttonPressed; 127 128 129 private final JButton _okButton = new JButton("OK"); 130 131 132 private final JTextField _textField = new JTextField(); 133 134 135 private final JLabel _tabCompletesLabel = new JLabel("Tab completes: "); 136 137 138 private final JList _matchList; 139 140 141 private final boolean _force; 142 143 144 private final JLabel _sharedExtLabel = new JLabel(""); 145 146 147 private final PredictiveInputListener _listener = new PredictiveInputListener(); 148 149 150 private final InfoSupplier<? super T> _info; 151 152 153 private final JLabel _infoLabel = new JLabel(""); 154 155 156 private final Frame _owner; 157 158 159 private final CloseAction<T> _okAction; 160 161 162 private final CloseAction<T> _cancelAction; 163 164 165 private final java.util.List <PredictiveInputModel.MatchingStrategy<T>> _strategies; 166 167 168 private final JComboBox _strategyBox; 169 170 171 private volatile FrameState _lastState; 172 173 174 private volatile PredictiveInputModel.MatchingStrategy<T> _currentStrategy; 175 176 186 187 public PredictiveInputFrame(Frame owner, String title, boolean force, boolean ignoreCase, InfoSupplier<? super T> info, 188 java.util.List <PredictiveInputModel.MatchingStrategy<T>> strategies, 189 CloseAction<T> okAction, CloseAction<T> cancelAction, java.util.List <T> items) { 190 super(title); 191 _strategies = strategies; 192 _strategyBox = new JComboBox(_strategies.toArray()); 193 _currentStrategy = _strategies.get(0); 194 _pim = new PredictiveInputModel<T>(ignoreCase, _currentStrategy, items); 195 _matchList = new JList(_pim.getMatchingItems().toArray()); 196 _force = force; 197 _info = info; 198 _lastState = null; 199 _owner = owner; 200 _okAction = okAction; 201 _cancelAction = cancelAction; 202 init(_info != null); 203 } 204 205 214 public PredictiveInputFrame(Frame owner, String title, boolean force, boolean ignoreCase, InfoSupplier<? super T> info, 215 List <PredictiveInputModel.MatchingStrategy<T>> strategies, 216 CloseAction<T> okAction, CloseAction<T> cancelAction, T... items) { 217 this(owner, title, force, ignoreCase, info, strategies, okAction, cancelAction, Arrays.asList(items)); 218 } 230 231 234 public FrameState getFrameState() { return _lastState; } 235 236 239 public void setFrameState(FrameState ds) { 240 _lastState = ds; 241 if (_lastState!=null) { 242 setSize(_lastState.getDimension()); 243 setLocation(_lastState.getLocation()); 244 int index = _lastState.getCurrentStrategyIndex(); 245 if ((index>=0) && (index<_strategies.size())) { 246 _currentStrategy = _strategies.get(index); 247 _strategyBox.setSelectedIndex(index); 248 } 249 selectStrategy(); 250 validate(); 251 } 252 } 253 254 257 public void setFrameState(String s) { 258 try { _lastState = new FrameState(s); } 259 catch(IllegalArgumentException e) { _lastState = null; } 260 if (_lastState!=null) { 261 setSize(_lastState.getDimension()); 262 setLocation(_lastState.getLocation()); 263 int index = _lastState.getCurrentStrategyIndex(); 264 if ((index>=0) && (index<_strategies.size())) { 265 _currentStrategy = _strategies.get(index); 266 _strategyBox.setSelectedIndex(index); 267 } 268 selectStrategy(); 269 validate(); 270 } 271 else { 272 Dimension parentDim = (_owner != null) ? _owner.getSize() : getToolkit().getScreenSize(); 273 int xs = (int)parentDim.getWidth()/3; 274 int ys = (int)parentDim.getHeight()/4; 275 setSize(Math.max(xs,400), Math.max(ys, 300)); 276 setLocationRelativeTo(_owner); 277 _currentStrategy = _strategies.get(0); 278 _strategyBox.setSelectedIndex(0); 279 selectStrategy(); 280 } 281 } 282 283 287 public void setModel(boolean ignoreCase, PredictiveInputModel<T> pim) { 288 _pim = new PredictiveInputModel<T>(ignoreCase, pim); 289 removeListener(); 290 updateTextField(); 291 updateExtensionLabel(); 292 updateList(); 293 addListener(); 294 } 295 296 300 public void setItems(boolean ignoreCase, List <T> items) { 301 _pim = new PredictiveInputModel<T>(ignoreCase, _currentStrategy, items); 302 removeListener(); 303 updateTextField(); 304 updateExtensionLabel(); 305 updateList(); 306 addListener(); 307 } 308 309 312 public void setCurrentItem(T item) { 313 _pim.setCurrentItem(item); 314 removeListener(); 315 updateTextField(); 316 updateExtensionLabel(); 317 updateList(); 318 addListener(); 319 } 320 321 325 public void setItems(boolean ignoreCase, T... items) { 326 _pim = new PredictiveInputModel<T>(ignoreCase, _currentStrategy, items); 327 removeListener(); 328 updateTextField(); 329 updateExtensionLabel(); 330 updateList(); 331 addListener(); 332 } 333 334 338 public int getButtonPressed() { 339 return _buttonPressed; 340 } 341 342 346 public String getText() { 347 if (_force) { 348 @SuppressWarnings ("unchecked") 349 T item = (T)_matchList.getSelectedValue(); 350 return (item==null)?"":_currentStrategy.force(item,_textField.getText()); 351 } 352 return _textField.getText(); 353 } 354 355 358 public T getItem() { 359 if (!_force && _pim.getMatchingItems().size() == 0) return null; 360 @SuppressWarnings ("unchecked") 361 T item = (T)_matchList.getSelectedValue(); 362 return item; 363 } 364 365 368 private void init(boolean info) { 369 _buttonPressed = JOptionPane.CANCEL_OPTION; 370 addWindowListener(new java.awt.event.WindowAdapter () { 371 public void windowClosing(WindowEvent winEvt) { 372 cancelButtonPressed(); 373 } 374 }); 375 addComponentListener(new java.awt.event.ComponentAdapter () { 376 public void componentResized(ComponentEvent e) { 377 validate(); 378 _matchList.ensureIndexIsVisible(_matchList.getSelectedIndex()); 379 } 380 }); 381 382 _okButton.addActionListener(new ActionListener() { 384 public void actionPerformed(ActionEvent e) { okButtonPressed(); } 385 }); 386 387 getRootPane().setDefaultButton(_okButton); 388 389 final JButton cancelButton = new JButton("Cancel"); 390 cancelButton.addActionListener(new ActionListener() { 391 public void actionPerformed(ActionEvent e) { 392 cancelButtonPressed(); 393 } 394 }); 395 396 _strategyBox.setEditable(false); 397 _strategyBox.addActionListener(new ActionListener() { 398 public void actionPerformed(ActionEvent e) { 399 selectStrategy(); 401 } 402 }); 403 _strategyBox.addFocusListener(new FocusListener() { 404 public void focusGained(FocusEvent e) { } 405 406 public void focusLost(FocusEvent e) { 407 if ((e.getOppositeComponent() != _textField) && 408 (e.getOppositeComponent() != _okButton) && 409 (e.getOppositeComponent() != cancelButton)) { 410 _textField.requestFocus(); 411 } 412 } 413 }); 414 415 _textField.setDragEnabled(false); 417 _textField.setFocusTraversalKeysEnabled(false); 418 419 addListener(); 420 421 Keymap ourMap = JTextComponent.addKeymap("PredictiveInputFrame._textField", _textField.getKeymap()); 422 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), new AbstractAction() { 423 public void actionPerformed(ActionEvent e) { 424 cancelButtonPressed(); 426 } 427 }); 428 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), new AbstractAction() { 429 public void actionPerformed(ActionEvent e) { 430 okButtonPressed(); 432 } 433 }); 434 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), new AbstractAction() { 435 public void actionPerformed(ActionEvent e) { 436 removeListener(); 438 _pim.extendSharedMask(); 439 updateTextField(); 440 updateExtensionLabel(); 441 updateList(); 442 addListener(); 443 } 444 }); 445 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new AbstractAction() { 446 public void actionPerformed(ActionEvent e) { 447 if (_matchList.getModel().getSize() > 0) { 449 removeListener(); 450 int i = _matchList.getSelectedIndex(); 451 if (i > 0) { 452 _matchList.setSelectedIndex(i - 1); 453 _matchList.ensureIndexIsVisible(i - 1); 454 _pim.setCurrentItem(_pim.getMatchingItems().get(i - 1)); 455 updateInfo(); 456 } 457 addListener(); 458 } 459 } 460 }); 461 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new AbstractAction() { 462 public void actionPerformed(ActionEvent e) { 463 if (_matchList.getModel().getSize()>0) { 465 removeListener(); 466 int i = _matchList.getSelectedIndex(); 467 if (i < _matchList.getModel().getSize() - 1) { 468 _matchList.setSelectedIndex(i + 1); 469 _matchList.ensureIndexIsVisible(i + 1); 470 _pim.setCurrentItem(_pim.getMatchingItems().get(i + 1)); 471 updateInfo(); 472 } 473 addListener(); 474 } 475 } 476 }); 477 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), new AbstractAction() { 478 public void actionPerformed(ActionEvent e) { 479 if (_matchList.getModel().getSize() > 0) { 481 removeListener(); 482 int page = _matchList.getLastVisibleIndex() - _matchList.getFirstVisibleIndex() + 1; 483 int i = _matchList.getSelectedIndex() - page; 484 if (i < 0) i = 0; 485 _matchList.setSelectedIndex(i); 486 _matchList.ensureIndexIsVisible(i); 487 _pim.setCurrentItem(_pim.getMatchingItems().get(i)); 488 updateInfo(); 489 addListener(); 490 } 491 } 492 }); 493 ourMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), new AbstractAction() { 494 public void actionPerformed(ActionEvent e) { 495 if (_matchList.getModel().getSize()>0) { 497 removeListener(); 498 int page = _matchList.getLastVisibleIndex() - _matchList.getFirstVisibleIndex() + 1; 499 int i = _matchList.getSelectedIndex() + page; 500 if (i >= _matchList.getModel().getSize()) { 501 i = _matchList.getModel().getSize() - 1; 502 } 503 _matchList.setSelectedIndex(i); 504 _matchList.ensureIndexIsVisible(i); 505 _pim.setCurrentItem(_pim.getMatchingItems().get(i)); 506 updateInfo(); 507 addListener(); 508 } 509 } 510 }); 511 _textField.setKeymap(ourMap); 512 513 530 _textField.addFocusListener(new FocusListener() { 531 public void focusGained(FocusEvent e) { } 532 533 public void focusLost(FocusEvent e) { 534 if ((e.getOppositeComponent() != _strategyBox) && 535 (e.getOppositeComponent() != _okButton) && 536 (e.getOppositeComponent() != cancelButton)) { 537 _textField.requestFocus(); 538 } 539 } 540 }); 541 542 _matchList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 543 _matchList.addListSelectionListener(new ListSelectionListener() { 544 public void valueChanged(ListSelectionEvent e) { 545 removeListener(); 547 int i = _matchList.getSelectedIndex(); 548 if (i >= 0) { 549 _pim.setCurrentItem(_pim.getMatchingItems().get(i)); 550 _matchList.ensureIndexIsVisible(i); 551 updateInfo(); 552 } 553 addListener(); 554 } 555 }); 556 557 Container contentPane = getContentPane(); 559 560 GridBagLayout layout = new GridBagLayout(); 561 contentPane.setLayout(layout); 562 563 GridBagConstraints c = new GridBagConstraints(); 564 c.anchor = GridBagConstraints.NORTHWEST; 565 c.weightx = 1.0; 566 c.weighty = 0.0; 567 c.gridwidth = GridBagConstraints.REMAINDER; c.insets.top = 2; 569 c.insets.left = 2; 570 c.insets.bottom = 2; 571 c.insets.right = 2; 572 573 if (info) { 574 c.fill = GridBagConstraints.NONE; 575 contentPane.add(_infoLabel, c); 576 } 577 578 c.fill = GridBagConstraints.BOTH; 579 c.weighty = 1.0; 580 contentPane.add(new JScrollPane(_matchList, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 581 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), 582 c); 583 584 c.anchor = GridBagConstraints.SOUTHWEST; 585 c.fill = GridBagConstraints.NONE; 586 c.weightx = 0.0; 587 c.weighty = 0.0; 588 c.gridwidth = 1; 589 contentPane.add(_tabCompletesLabel, c); 590 591 c.fill = GridBagConstraints.HORIZONTAL; 592 c.weightx = 1.0; 593 c.gridwidth = GridBagConstraints.REMAINDER; 594 contentPane.add(_sharedExtLabel, c); 595 596 contentPane.add(_textField, c); 597 598 c.anchor = GridBagConstraints.SOUTH; 599 600 JPanel buttonPanel = new JPanel(new GridBagLayout()); 601 GridBagConstraints bc = new GridBagConstraints(); 602 bc.insets.left = 2; 603 bc.insets.right = 2; 604 buttonPanel.add(new JLabel("Matching strategy:"), bc); 605 buttonPanel.add(_strategyBox, bc); 606 buttonPanel.add(_okButton, bc); 607 buttonPanel.add(cancelButton, bc); 608 609 contentPane.add(buttonPanel, c); 610 611 Dimension parentDim = (_owner!=null)?(_owner.getSize()):getToolkit().getScreenSize(); 612 int xs = (int)parentDim.getWidth()/3; 613 int ys = (int)parentDim.getHeight()/4; 614 setSize(Math.max(xs,400), Math.max(ys, 300)); 615 setLocationRelativeTo(_owner); 616 617 removeListener(); 618 updateTextField(); 619 addListener(); 620 updateList(); 621 } 622 623 627 public void setOwnerEnabled(boolean b) { 628 } 630 631 634 public void setVisible(boolean b) { 635 validate(); 636 super.setVisible(b); 637 if (b) { 638 setOwnerEnabled(false); 639 _textField.requestFocus(); 640 } 641 else { 642 setOwnerEnabled(true); 643 _owner.toFront(); 644 } 645 } 646 647 648 private void addListener() { 649 _textField.getDocument().addDocumentListener(_listener); 650 _textField.addCaretListener(_listener); 651 } 652 653 654 private void removeListener() { 655 _textField.getDocument().removeDocumentListener(_listener); 656 _textField.removeCaretListener(_listener); 657 } 658 659 660 private void updateTextField() { 661 _textField.setText(_pim.getMask()); 662 _textField.setCaretPosition(_pim.getMask().length()); 663 } 664 665 666 private void updateExtensionLabel() { 667 _sharedExtLabel.setText(_pim.getSharedMaskExtension()+" "); 668 _tabCompletesLabel.setVisible(_pim.getSharedMaskExtension().length()>0); 669 } 670 671 672 private void updateList() { 673 _matchList.setListData(_pim.getMatchingItems().toArray()); 674 _matchList.setSelectedValue(_pim.getCurrentItem(), true); 675 updateExtensionLabel(); 676 updateInfo(); 677 _okButton.setEnabled(_matchList.getModel().getSize()>0); 678 } 679 680 681 private void updateInfo() { 682 if (_info == null) return; 683 if (_matchList.getModel().getSize()>0) { 684 @SuppressWarnings ("unchecked") T item = (T)_matchList.getSelectedValue(); 685 _infoLabel.setText("Path: " + _info.apply(item)); 686 } 687 else _infoLabel.setText("No file selected"); 688 } 689 690 691 private void okButtonPressed() { 692 if (_matchList.getModel().getSize()>0) { 693 _buttonPressed = JOptionPane.OK_OPTION; 694 _lastState = new FrameState(PredictiveInputFrame.this); 695 setVisible(false); 696 _okAction.apply(this); 697 } 698 else Toolkit.getDefaultToolkit().beep(); 699 } 700 701 702 private void cancelButtonPressed() { 703 _buttonPressed = JOptionPane.CANCEL_OPTION; 704 _lastState = new FrameState(PredictiveInputFrame.this); 705 setVisible(false); 706 _cancelAction.apply(this); 707 } 708 709 710 public void selectStrategy() { 711 _currentStrategy = _strategies.get(_strategyBox.getSelectedIndex()); 712 removeListener(); 713 _pim.setStrategy(_currentStrategy); 714 updateTextField(); 715 updateExtensionLabel(); 716 updateList(); 717 addListener(); 718 _textField.requestFocus(); 719 } 720 721 722 private class PredictiveInputListener implements CaretListener, DocumentListener { 723 public void insertUpdate(DocumentEvent e) { 724 Utilities.invokeLater(new Runnable () { 726 public void run() { 727 removeListener(); 728 _pim.setMask(_textField.getText()); 729 updateExtensionLabel(); 730 updateList(); 731 addListener(); 732 } 733 }); 734 } 735 736 public void removeUpdate(DocumentEvent e) { 737 Utilities.invokeLater(new Runnable () { 739 public void run() { 740 removeListener(); 741 _pim.setMask(_textField.getText()); 742 updateExtensionLabel(); 743 updateList(); 744 addListener(); 745 } 746 }); 747 } 748 749 public void changedUpdate(DocumentEvent e) { 750 Utilities.invokeLater(new Runnable () { 752 public void run() { 753 removeListener(); 754 _pim.setMask(_textField.getText()); 755 updateExtensionLabel(); 756 updateList(); 757 addListener(); 758 } 759 }); 760 } 761 762 public void caretUpdate(CaretEvent e) { } 763 } 764 } 765 | Popular Tags |