Source Code Cross Referenced for Searchable.java in  » Swing-Library » jide-common » com » jidesoft » swing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » jide common » com.jidesoft.swing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * @(#)${NAME}
0003:         *
0004:         * Copyright 2002 - 2004 JIDE Software Inc. All rights reserved.
0005:         */
0006:        package com.jidesoft.swing;
0007:
0008:        import com.jidesoft.plaf.UIDefaultsLookup;
0009:        import com.jidesoft.popup.JidePopup;
0010:        import com.jidesoft.swing.event.SearchableEvent;
0011:        import com.jidesoft.swing.event.SearchableListener;
0012:
0013:        import javax.swing.*;
0014:        import javax.swing.event.DocumentEvent;
0015:        import javax.swing.event.DocumentListener;
0016:        import javax.swing.event.EventListenerList;
0017:        import java.awt.*;
0018:        import java.awt.event.*;
0019:        import java.beans.PropertyChangeListener;
0020:        import java.beans.PropertyChangeSupport;
0021:        import java.util.Locale;
0022:        import java.util.regex.Pattern;
0023:        import java.util.regex.PatternSyntaxException;
0024:
0025:        /**
0026:         * JList, JTable and JTree are three data-rich components. They can be used
0027:         * to display a huge amount of data so searching function will be very a useful feature in those components.
0028:         * <code>Searchable</code> is such a class that can make JList, JTable and JTree searchable.
0029:         * User can simply type in any string they want to search for and use arrow keys to navigate
0030:         * to next or previous occurrence.
0031:         * <p/>
0032:         * <code>Searchable</code> is a base abstract class. <code>ListSearchable</code>, <code>TableSearchable</code> and <code>TreeSearchable</code>
0033:         * are implementations to make JList, JTable and JTree searchable respectively. For each implementation, there are
0034:         * five methods need to be implemented.
0035:         * <ul>
0036:         * <li><code>protected abstract int getSelectedIndex()</code>
0037:         * <li><code>protected abstract void setSelectedIndex(int index, boolean incremental)</code>
0038:         * <li><code>protected abstract int getElementCount()</code>
0039:         * <li><code>protected abstract Object getElementAt(int index)</code>
0040:         * <li><code>protected abstract String convertElementToString(Object element)</code>
0041:         * </ul>
0042:         * <p/>
0043:         * Please look at the javadoc of each method to learn more details.
0044:         * <p/>
0045:         * The keys used by this class are fully customizable. Subclass can override the methods such as {@link #isActivateKey(java.awt.event.KeyEvent)},
0046:         * {@link #isDeactivateKey(java.awt.event.KeyEvent)}, {@link #isFindFirstKey(java.awt.event.KeyEvent)},{@link #isFindLastKey(java.awt.event.KeyEvent)},
0047:         * {@link #isFindNextKey(java.awt.event.KeyEvent)}, {@link #isFindPreviousKey(java.awt.event.KeyEvent)} to provide its own set of keys.
0048:         * <p/>
0049:         * In addition to press up/down arrow to find next occurrence or previous occurrence of
0050:         * particular string, there are several other features that are very handy.
0051:         * <p/>
0052:         * Multiple selection feature - If you press CTRL key and hold it while pressing up and down arrow,
0053:         * it will find next/previous occurence while keeping existing selections.
0054:         * <br>
0055:         * Select all feature - If you type in a searching text and press CTRL+A, all the occurrences of
0056:         * that searching string will be selected. This is a very handy feature.
0057:         * For example you want to delete all rows in a table whose name column begins with "old".
0058:         * So you can type in "old" and press CTRL+A, now all rows begining with "old" will
0059:         * be selected. Pressing delete will delete all of them.
0060:         * <br>
0061:         * Basic regular expression support - It allows '?' to match any letter or digit,
0062:         * or '*' to match several letters or digits.
0063:         * Even though it's possible to implement full regular expression support, we don't want to do that.
0064:         * The reason is the regular expression is very complex, it's probably not a good idea to let user
0065:         * type in such a complex expression in a small popup window. However if your user is very familiar
0066:         * with regular expression, you can add the feature to <code>Searchable</code>. All you need to do
0067:         * is to override {@link #compare(String,String)} method
0068:         * and implement by yourself.
0069:         * <p/>
0070:         * As this is an abstract class, please refer to to javadoc of {@link ListSearchable},{@link TreeSearchable}, and {@link TableSearchable} to find out
0071:         * how to use it with JList, JTree and JTable respectively.
0072:         * <p/>
0073:         * This component has a timer. If user types very fast, it will accumulate them together and generate only one searching action.
0074:         * The timer can be controlled by {@link #setSearchingDelay(int)}.
0075:         * <p/>
0076:         * By default we will use lightweight popup for the sake of performance. But if you use heavyweight component
0077:         * which could obscure the lightweight popup, you can call {@link #setHeavyweightComponentEnabled(boolean)} to true
0078:         * so that heavyweight popup will be used.
0079:         */
0080:        public abstract class Searchable {
0081:
0082:            private final PropertyChangeSupport _propertyChangeSupport = new PropertyChangeSupport(
0083:                    this );
0084:
0085:            protected final JComponent _component;
0086:
0087:            private SearchPopup _popup;
0088:            private JLayeredPane _layeredPane;
0089:
0090:            private boolean _heavyweightComponentEnabled;
0091:
0092:            /**
0093:             * optional SearchableProvider
0094:             */
0095:            private SearchableProvider _searchableProvider;
0096:            private Pattern _pattern;
0097:            private String _searchText;
0098:            private String _previousSearchText;
0099:
0100:            private boolean _caseSensitive = false;
0101:            private boolean _repeats = false;
0102:            private boolean _wildcardEnabled = true;
0103:            private Color _mismatchForeground;
0104:            private Color _foreground = null;
0105:            private Color _background = null;
0106:            protected ComponentAdapter _componentListener;
0107:
0108:            public final static String PROPERTY_SEARCH_TEXT = "searchText";
0109:
0110:            private KeyAdapter _keyListener;
0111:
0112:            private FocusAdapter _focusListener;
0113:
0114:            private int _cursor = -1;
0115:
0116:            private String _searchLabel = getResourceString("Searchable.searchFor");
0117:
0118:            private String _noMatchLabel = getResourceString("Searchable.noMatch");
0119:
0120:            /**
0121:             * The popup location
0122:             */
0123:            private int _popupLocation = SwingConstants.TOP;
0124:
0125:            private int _searchingDelay = 0;
0126:
0127:            private boolean _reverseOrder = false;
0128:
0129:            /**
0130:             * A list of event listeners for this component.
0131:             */
0132:            protected EventListenerList listenerList = new EventListenerList();
0133:
0134:            private Component _popupLocationRelativeTo;
0135:
0136:            /**
0137:             * Creates a Searchable.
0138:             *
0139:             * @param component component where the Searchable will be installed.
0140:             */
0141:            public Searchable(JComponent component) {
0142:                _previousSearchText = null;
0143:                _component = component;
0144:                installListeners();
0145:            }
0146:
0147:            /**
0148:             * Creates a Searchable.
0149:             *
0150:             * @param component component where the Searchable will be installed.
0151:             */
0152:            public Searchable(JComponent component,
0153:                    SearchableProvider searchableProvider) {
0154:                _searchableProvider = searchableProvider;
0155:                _previousSearchText = null;
0156:                _component = component;
0157:                installListeners();
0158:            }
0159:
0160:            /**
0161:             * Gets the selected index in the component. The concrete implementation
0162:             * should call methods on the component to retrieve the current selected index.
0163:             * If the component supports multiple selection,
0164:             * it's OK just return the index of the first selection.
0165:             * <p>Here are some examples. In the case of JList, the index is the row index.
0166:             * In the case of JTree, the index is the row index too. In the case of JTable, depending on the seleection mode,
0167:             * the index could be row index (in row selection mode), could be column index (in column selection mode)
0168:             * or could the cell index (in cell selection mode).
0169:             *
0170:             * @return the selected index.
0171:             */
0172:            protected abstract int getSelectedIndex();
0173:
0174:            /**
0175:             * Sets the selected index. The concrete implementation should call methods on the component to select
0176:             * the element at the specified index. The incremental flag is used to do multiple select. If the flag is true,
0177:             * the element at the index should be added to current selection. If false, you should clear previous
0178:             * selection and then select the element.
0179:             *
0180:             * @param index       the index to be selected
0181:             * @param incremental a flag to enable multiple selection. If the flag is true,
0182:             *                    the element at the index should be added to current selection. If false, you should clear previous
0183:             *                    selection and then select the element.
0184:             */
0185:            protected abstract void setSelectedIndex(int index,
0186:                    boolean incremental);
0187:
0188:            /**
0189:             * Gets the total element count in the component. Different concrete implementation could have different interpretation of
0190:             * the count. This is totally OK as long as it's consistent in all the methods. For example, the index parameter in other methods
0191:             * should be always a valid value within the total count.
0192:             *
0193:             * @return the total element count.
0194:             */
0195:            protected abstract int getElementCount();
0196:
0197:            /**
0198:             * Gets the element at the specified index. The element could be any data structure that
0199:             * internally used in the component. The convertElementToString method will give you a chance
0200:             * to convert the element to string which is used to compare with the string that user types in.
0201:             *
0202:             * @param index the index
0203:             * @return the element at the specified index.
0204:             */
0205:            protected abstract Object getElementAt(int index);
0206:
0207:            /**
0208:             * Converts the element that returns from getElementAt() to string.
0209:             *
0210:             * @param element
0211:             * @return the string representing the element in the component.
0212:             */
0213:            protected abstract String convertElementToString(Object element);
0214:
0215:            /**
0216:             * A text field for searching text.
0217:             */
0218:            protected class SearchField extends JTextField {
0219:                SearchField() {
0220:                }
0221:
0222:                @Override
0223:                public Dimension getPreferredSize() {
0224:                    Dimension size = super .getPreferredSize();
0225:                    size.width = getFontMetrics(getFont()).stringWidth(
0226:                            getText()) + 4;
0227:                    return size;
0228:                }
0229:
0230:                @Override
0231:                public void processKeyEvent(KeyEvent e) {
0232:                    int keyCode = e.getKeyCode();
0233:                    if (keyCode == KeyEvent.VK_BACK_SPACE
0234:                            && getDocument().getLength() == 0) {
0235:                        e.consume();
0236:                        return;
0237:                    }
0238:                    final boolean isNavigationKey = isNavigationKey(e);
0239:                    if (isDeactivateKey(e) && !isNavigationKey) {
0240:                        hidePopup();
0241:                        if (keyCode == KeyEvent.VK_ESCAPE)
0242:                            e.consume();
0243:                        return;
0244:                    }
0245:                    super .processKeyEvent(e);
0246:                    if (keyCode == KeyEvent.VK_BACK_SPACE || isNavigationKey)
0247:                        e.consume();
0248:                    if (isSelectAllKey(e)) {
0249:                        e.consume();
0250:                    }
0251:                }
0252:            }
0253:
0254:            /**
0255:             * The popup panel for search label and search text field.
0256:             */
0257:            private class DefaultSearchPopup extends SearchPopup {
0258:                private JLabel _label;
0259:                private JLabel _noMatch;
0260:
0261:                public DefaultSearchPopup(String text) {
0262:                    initComponents(text);
0263:                }
0264:
0265:                private void initComponents(String text) {
0266:                    final Color foreground = Searchable.this .getForeground();
0267:                    final Color background = Searchable.this .getBackground();
0268:
0269:                    // setup the label
0270:                    _label = new JLabel(_searchLabel);
0271:                    _label.setForeground(foreground);
0272:                    _label.setVerticalAlignment(JLabel.BOTTOM);
0273:
0274:                    _noMatch = new JLabel();
0275:                    _noMatch.setForeground(getMismatchForeground());
0276:                    _noMatch.setVerticalAlignment(JLabel.BOTTOM);
0277:
0278:                    //setup text field
0279:                    _textField = new SearchField();
0280:                    _textField.setFocusable(false);
0281:                    _textField.setOpaque(false);
0282:                    _textField.setBorder(BorderFactory.createEmptyBorder());
0283:                    _textField.setForeground(foreground);
0284:                    _textField.setCursor(getCursor());
0285:                    _textField.getDocument().addDocumentListener(
0286:                            new DocumentListener() {
0287:                                private Timer timer = new Timer(200,
0288:                                        new ActionListener() {
0289:                                            public void actionPerformed(
0290:                                                    ActionEvent e) {
0291:                                                applyText();
0292:                                            }
0293:                                        });
0294:
0295:                                public void insertUpdate(DocumentEvent e) {
0296:                                    startTimer();
0297:                                }
0298:
0299:                                public void removeUpdate(DocumentEvent e) {
0300:                                    startTimer();
0301:                                }
0302:
0303:                                public void changedUpdate(DocumentEvent e) {
0304:                                    startTimer();
0305:                                }
0306:
0307:                                protected void applyText() {
0308:                                    String text = _textField.getText().trim();
0309:                                    if (text.length() != 0) {
0310:                                        int found = findFromCursor(text);
0311:                                        if (found == -1) {
0312:                                            _textField
0313:                                                    .setForeground(getMismatchForeground());
0314:                                        } else {
0315:                                            _textField
0316:                                                    .setForeground(foreground);
0317:                                        }
0318:                                        select(found, null, text);
0319:                                    } else {
0320:                                        hidePopup();
0321:                                    }
0322:                                }
0323:
0324:                                void startTimer() {
0325:                                    updatePopupBounds();
0326:                                    if (getSearchingDelay() > 0) {
0327:                                        timer.setDelay(getSearchingDelay());
0328:                                        if (timer.isRunning()) {
0329:                                            timer.restart();
0330:                                        } else {
0331:                                            timer.setRepeats(false);
0332:                                            timer.start();
0333:                                        }
0334:                                    } else {
0335:                                        applyText();
0336:                                    }
0337:                                }
0338:                            });
0339:                    _textField.setText(text);
0340:
0341:                    setBackground(background);
0342:                    setBorder(BorderFactory.createCompoundBorder(BorderFactory
0343:                            .createLineBorder(UIDefaultsLookup
0344:                                    .getColor("controlShadow"), 1),
0345:                            BorderFactory.createEmptyBorder(0, 6, 1, 8)));
0346:                    setLayout(new BorderLayout(2, 0));
0347:                    Dimension size = _label.getPreferredSize();
0348:                    size.height = _textField.getPreferredSize().height;
0349:                    _label.setPreferredSize(size);
0350:                    add(_label, BorderLayout.BEFORE_LINE_BEGINS);
0351:                    add(_textField, BorderLayout.CENTER);
0352:                    add(_noMatch, BorderLayout.AFTER_LINE_ENDS);
0353:                    setPopupBorder(BorderFactory.createEmptyBorder());
0354:                }
0355:
0356:                @Override
0357:                protected void select(int index, KeyEvent e,
0358:                        String searchingText) {
0359:                    if (index != -1) {
0360:                        setSelectedIndex(index, e != null
0361:                                && isIncrementalSelectKey(e));
0362:                        _cursor = index;
0363:                        _textField.setForeground(getForeground());
0364:                        _noMatch.setText("");
0365:                    } else {
0366:                        _textField.setForeground(getMismatchForeground());
0367:                        _noMatch.setText(_noMatchLabel);
0368:                    }
0369:                    updatePopupBounds();
0370:                    firePropertyChangeEvent(searchingText);
0371:                    if (index != -1) {
0372:                        Object element = getElementAt(index);
0373:                        fireSearchableEvent(new SearchableEvent(
0374:                                Searchable.this ,
0375:                                SearchableEvent.SEARCHABLE_MATCH,
0376:                                searchingText, element,
0377:                                convertElementToString(element)));
0378:                    } else {
0379:                        fireSearchableEvent(new SearchableEvent(
0380:                                Searchable.this ,
0381:                                SearchableEvent.SEARCHABLE_NOMATCH,
0382:                                searchingText));
0383:                    }
0384:                }
0385:
0386:                private void updatePopupBounds() {
0387:                    if (_popup != null) {
0388:                        _textField.invalidate();
0389:                        try {
0390:                            if (!isHeavyweightComponentEnabled()) {
0391:                                _popup.setSize(_popup.getPreferredSize());
0392:                                _popup.validate();
0393:                            } else {
0394:                                _popup.packPopup();
0395:                            }
0396:                        } catch (Exception e) { // catch any potential exception
0397:                            // see bug report at http://www.jidesoft.com/forum/viewtopic.php?p=8557#8557
0398:                        }
0399:                    }
0400:                }
0401:            }
0402:
0403:            /**
0404:             * Hides the popup.
0405:             */
0406:            public void hidePopup() {
0407:                if (_popup != null) {
0408:                    if (isHeavyweightComponentEnabled()) {
0409:                        _popup.hidePopupImmediately();
0410:                    } else {
0411:                        _layeredPane.remove(_popup);
0412:                        _layeredPane.validate();
0413:                        _layeredPane.repaint();
0414:                        _layeredPane = null;
0415:                    }
0416:                    _popup = null;
0417:                    _searchableProvider = null;
0418:                    fireSearchableEvent(new SearchableEvent(this ,
0419:                            SearchableEvent.SEARCHABLE_END));
0420:                }
0421:                _cursor = -1;
0422:            }
0423:
0424:            public SearchableProvider getSearchableProvider() {
0425:                return _searchableProvider;
0426:            }
0427:
0428:            public void setSearchableProvider(
0429:                    SearchableProvider searchableProvider) {
0430:                _searchableProvider = searchableProvider;
0431:            }
0432:
0433:            /**
0434:             * Installs necessary listeners to the component. This method will be called automatically when Searchable is created.
0435:             */
0436:            public void installListeners() {
0437:                if (_componentListener == null) {
0438:                    _componentListener = new ComponentAdapter() {
0439:                        @Override
0440:                        public void componentHidden(ComponentEvent e) {
0441:                            super .componentHidden(e);
0442:                            boolean passive = _searchableProvider == null
0443:                                    || _searchableProvider.isPassive();
0444:                            if (passive) {
0445:                                hidePopup();
0446:                            }
0447:                        }
0448:
0449:                        @Override
0450:                        public void componentResized(ComponentEvent e) {
0451:                            super .componentResized(e);
0452:                            boolean passive = _searchableProvider == null
0453:                                    || _searchableProvider.isPassive();
0454:                            if (passive) {
0455:                                updateSizeAndLocation();
0456:                            }
0457:                        }
0458:
0459:                        @Override
0460:                        public void componentMoved(ComponentEvent e) {
0461:                            super .componentMoved(e);
0462:                            boolean passive = _searchableProvider == null
0463:                                    || _searchableProvider.isPassive();
0464:                            if (passive) {
0465:                                updateSizeAndLocation();
0466:                            }
0467:                        }
0468:                    };
0469:                }
0470:                _component.addComponentListener(_componentListener);
0471:                Component scrollPane = JideSwingUtilities
0472:                        .getScrollPane(_component);
0473:                if (scrollPane != null) {
0474:                    scrollPane.addComponentListener(_componentListener);
0475:                }
0476:
0477:                _keyListener = new KeyAdapter() {
0478:                    @Override
0479:                    public void keyTyped(KeyEvent e) {
0480:                        boolean passive = _searchableProvider == null
0481:                                || _searchableProvider.isPassive();
0482:                        if (passive) {
0483:                            keyTypedOrPressed(e);
0484:                        }
0485:                    }
0486:
0487:                    @Override
0488:                    public void keyPressed(KeyEvent e) {
0489:                        boolean passive = _searchableProvider == null
0490:                                || _searchableProvider.isPassive();
0491:                        if (passive) {
0492:                            keyTypedOrPressed(e);
0493:                        }
0494:                    }
0495:                };
0496:                JideSwingUtilities.insertKeyListener(getComponent(),
0497:                        _keyListener, 0);
0498:
0499:                _focusListener = new FocusAdapter() {
0500:                    @Override
0501:                    public void focusLost(FocusEvent focusevent) {
0502:                        boolean passive = _searchableProvider == null
0503:                                || _searchableProvider.isPassive();
0504:                        if (passive) {
0505:                            hidePopup();
0506:                        }
0507:                    }
0508:                };
0509:                getComponent().addFocusListener(_focusListener);
0510:            }
0511:
0512:            /**
0513:             * Uninstall the listeners that installed before. This method is never called because
0514:             * we don't have the control of the life cyle of the component. However you can call this
0515:             * method if you don't want the searchable component not searchable.
0516:             */
0517:            public void uninstallListeners() {
0518:                if (_componentListener != null) {
0519:                    getComponent().removeComponentListener(_componentListener);
0520:                    Component scrollPane = JideSwingUtilities
0521:                            .getScrollPane(getComponent());
0522:                    if (scrollPane != null) {
0523:                        scrollPane.removeComponentListener(_componentListener);
0524:                    }
0525:                    _componentListener = null;
0526:                }
0527:
0528:                if (_keyListener != null) {
0529:                    getComponent().removeKeyListener(_keyListener);
0530:                    _keyListener = null;
0531:                }
0532:
0533:                if (_focusListener != null) {
0534:                    getComponent().removeFocusListener(_focusListener);
0535:                    _focusListener = null;
0536:                }
0537:            }
0538:
0539:            /**
0540:             * Adds the property change listener. The only property change event that will be fired is the "searchText"
0541:             * property which will be fired when user types in a different search text in the popup.
0542:             *
0543:             * @param propertychangelistener
0544:             */
0545:            public void addPropertyChangeListener(
0546:                    PropertyChangeListener propertychangelistener) {
0547:                _propertyChangeSupport
0548:                        .addPropertyChangeListener(propertychangelistener);
0549:            }
0550:
0551:            /**
0552:             * Removes the property change listener.
0553:             *
0554:             * @param propertychangelistener
0555:             */
0556:            public void removePropertyChangeListener(
0557:                    PropertyChangeListener propertychangelistener) {
0558:                _propertyChangeSupport
0559:                        .removePropertyChangeListener(propertychangelistener);
0560:            }
0561:
0562:            public void firePropertyChangeEvent(String searchingText) {
0563:                if (!searchingText.equals(_previousSearchText)) {
0564:                    _propertyChangeSupport.firePropertyChange(
0565:                            PROPERTY_SEARCH_TEXT, _previousSearchText,
0566:                            searchingText);
0567:                    fireSearchableEvent(new SearchableEvent(Searchable.this ,
0568:                            SearchableEvent.SEARCHABLE_CHANGE, searchingText,
0569:                            _previousSearchText));
0570:                    _previousSearchText = searchingText;
0571:                }
0572:            }
0573:
0574:            /**
0575:             * Checks if the element matches the searching text.
0576:             *
0577:             * @param element
0578:             * @param searchingText
0579:             * @return true if matches.
0580:             */
0581:            protected boolean compare(Object element, String searchingText) {
0582:                String text = convertElementToString(element);
0583:                return text != null
0584:                        && compare(isCaseSensitive() ? text : text
0585:                                .toLowerCase(), searchingText);
0586:            }
0587:
0588:            /**
0589:             * Checks if the element string matches the searching text. Different from {@link #compare(Object,String)},
0590:             * this method is after the element has been converted to string using {@link #convertElementToString(Object)}.
0591:             *
0592:             * @param text
0593:             * @param searchingText
0594:             * @return true if matches.
0595:             */
0596:            protected boolean compare(String text, String searchingText) {
0597:                if (!isWildcardEnabled()) {
0598:                    return searchingText != null
0599:                            && (searchingText.equals(text) || searchingText
0600:                                    .length() > 0
0601:                                    && text.startsWith(searchingText));
0602:                } else {
0603:                    // if it doesn't have the two special characters we support, we don't need to use regular expression.
0604:                    int posAny = searchingText.indexOf('*');
0605:                    int posOne = searchingText.indexOf('?');
0606:                    // 
0607:                    if ((posAny == -1/* || posAny == searchingText.length() - 1*/)
0608:                            && (posOne == -1/* || posOne == searchingText.length() - 1*/)) {
0609:                        return text.startsWith(searchingText);
0610:                    }
0611:
0612:                    // use the prvious pattern since nothing changed.
0613:                    if (_searchText != null
0614:                            && _searchText.equals(searchingText)
0615:                            && _pattern != null) {
0616:                        return _pattern.matcher(text).find();
0617:                    }
0618:
0619:                    _searchText = searchingText;
0620:                    StringBuffer buffer = new StringBuffer();
0621:                    int length = searchingText.length();
0622:                    buffer.append('^');
0623:                    for (int i = 0; i < length; i++) {
0624:                        char c = searchingText.charAt(i);
0625:                        // replace '?' with '.'
0626:                        if (c == '?') {
0627:                            buffer.append(".");
0628:                        } else if (c == '*') {
0629:                            buffer.append(".*");
0630:                        } else if ("(){}[].^$\\".indexOf(c) != -1) { // escape all other special characters
0631:                            buffer.append('\\');
0632:                            buffer.append(c);
0633:                        } else {
0634:                            buffer.append(c);
0635:                        }
0636:                    }
0637:                    try {
0638:                        _pattern = Pattern.compile(buffer.toString(),
0639:                                isCaseSensitive() ? 0
0640:                                        : Pattern.CASE_INSENSITIVE);
0641:                        return _pattern.matcher(text).find();
0642:                    } catch (PatternSyntaxException e) {
0643:                        return false;
0644:                    }
0645:                }
0646:            }
0647:
0648:            /**
0649:             * Gets the cursor which is the index of current location when searching. The value will be
0650:             * used in findNext and findPrevious.
0651:             *
0652:             * @return the current position of the cursor.
0653:             */
0654:            public int getCursor() {
0655:                return _cursor;
0656:            }
0657:
0658:            /**
0659:             * Sets the cursor which is the index of current location when searching. The value will be
0660:             * used in findNext and findPrevious.
0661:             *
0662:             * @param cursor the new position of the cursor.
0663:             */
0664:            public void setCursor(int cursor) {
0665:                _cursor = cursor;
0666:            }
0667:
0668:            /**
0669:             * Finds the next matching index from the cursor.
0670:             *
0671:             * @param s
0672:             * @return the next index that the element matches the searching text.
0673:             */
0674:            public int findNext(String s) {
0675:                String str = isCaseSensitive() ? s : s.toLowerCase();
0676:                int count = getElementCount();
0677:                if (count == 0)
0678:                    return s.length() > 0 ? -1 : 0;
0679:                int selectedIndex = (_cursor != -1 ? _cursor
0680:                        : getSelectedIndex());
0681:                for (int i = selectedIndex + 1; i < count; i++) {
0682:                    Object element = getElementAt(i);
0683:                    if (compare(element, str))
0684:                        return i;
0685:                }
0686:
0687:                if (isRepeats()) {
0688:                    for (int i = 0; i < selectedIndex; i++) {
0689:                        Object element = getElementAt(i);
0690:                        if (compare(element, str))
0691:                            return i;
0692:                    }
0693:                }
0694:
0695:                return selectedIndex == -1 ? -1 : (compare(
0696:                        getElementAt(selectedIndex), str) ? selectedIndex : -1);
0697:            }
0698:
0699:            /**
0700:             * Finds the previous matching index from the cursor.
0701:             *
0702:             * @param s
0703:             * @return the previous index that the element matches the searching text.
0704:             */
0705:            public int findPrevious(String s) {
0706:                String str = isCaseSensitive() ? s : s.toLowerCase();
0707:                int count = getElementCount();
0708:                if (count == 0)
0709:                    return s.length() > 0 ? -1 : 0;
0710:                int selectedIndex = (_cursor != -1 ? _cursor
0711:                        : getSelectedIndex());
0712:                for (int i = selectedIndex - 1; i >= 0; i--) {
0713:                    Object element = getElementAt(i);
0714:                    if (compare(element, str))
0715:                        return i;
0716:                }
0717:
0718:                if (isRepeats()) {
0719:                    for (int i = count - 1; i >= selectedIndex; i--) {
0720:                        Object element = getElementAt(i);
0721:                        if (compare(element, str))
0722:                            return i;
0723:                    }
0724:                }
0725:                return selectedIndex == -1 ? -1 : (compare(
0726:                        getElementAt(selectedIndex), str) ? selectedIndex : -1);
0727:            }
0728:
0729:            /**
0730:             * Finds the next matching index from the cursor. If it reaches the end, it will restart from the beginning.
0731:             * However is the reverseOrder flag is true, it will finds the previous matching index from the cursor. If it reaches
0732:             * the beginning, it will restart from the end.
0733:             *
0734:             * @param s
0735:             * @return the next index that the element matches the searching text.
0736:             */
0737:            public int findFromCursor(String s) {
0738:                if (isReverseOrder()) {
0739:                    return reverseFindFromCursor(s);
0740:                }
0741:
0742:                String str = isCaseSensitive() ? s : s.toLowerCase();
0743:                int selectedIndex = (_cursor != -1 ? _cursor
0744:                        : getSelectedIndex());
0745:                if (selectedIndex < 0)
0746:                    selectedIndex = 0;
0747:                int count = getElementCount();
0748:                if (count == 0)
0749:                    return -1; // no match
0750:
0751:                // find from cursor
0752:                for (int i = selectedIndex; i < count; i++) {
0753:                    Object element = getElementAt(i);
0754:                    if (compare(element, str))
0755:                        return i;
0756:                }
0757:
0758:                // if not found, start over from the beginning
0759:                for (int i = 0; i < selectedIndex; i++) {
0760:                    Object element = getElementAt(i);
0761:                    if (compare(element, str))
0762:                        return i;
0763:                }
0764:
0765:                return -1;
0766:            }
0767:
0768:            /**
0769:             * Finds the previous matching index from the cursor. If it reaches the beginning, it will restart from the end.
0770:             *
0771:             * @param s
0772:             * @return the next index that the element matches the searching text.
0773:             */
0774:            public int reverseFindFromCursor(String s) {
0775:                if (!isReverseOrder()) {
0776:                    return findFromCursor(s);
0777:                }
0778:
0779:                String str = isCaseSensitive() ? s : s.toLowerCase();
0780:                int selectedIndex = (_cursor != -1 ? _cursor
0781:                        : getSelectedIndex());
0782:                if (selectedIndex < 0)
0783:                    selectedIndex = 0;
0784:                int count = getElementCount();
0785:                if (count == 0)
0786:                    return -1; // no match
0787:
0788:                // find from cursor to beginning
0789:                for (int i = selectedIndex; i >= 0; i--) {
0790:                    Object element = getElementAt(i);
0791:                    if (compare(element, str))
0792:                        return i;
0793:                }
0794:
0795:                // if not found, start over from the end
0796:                for (int i = count - 1; i >= selectedIndex; i--) {
0797:                    Object element = getElementAt(i);
0798:                    if (compare(element, str))
0799:                        return i;
0800:                }
0801:
0802:                return -1;
0803:            }
0804:
0805:            /**
0806:             * Finds the first element that matches the searching text.
0807:             *
0808:             * @param s
0809:             * @return the first element that matches with the searching text.
0810:             */
0811:            public int findFirst(String s) {
0812:                String str = isCaseSensitive() ? s : s.toLowerCase();
0813:                int count = getElementCount();
0814:                if (count == 0)
0815:                    return s.length() > 0 ? -1 : 0;
0816:
0817:                for (int i = 0; i < count; i++) {
0818:                    int index = getIndex(count, i);
0819:                    Object element = getElementAt(index);
0820:                    if (compare(element, str))
0821:                        return index;
0822:                }
0823:
0824:                return -1;
0825:            }
0826:
0827:            /**
0828:             * Finds the last element that matches the searching text.
0829:             *
0830:             * @param s
0831:             * @return the last element that matches the searching text.
0832:             */
0833:            public int findLast(String s) {
0834:                String str = isCaseSensitive() ? s : s.toLowerCase();
0835:                int count = getElementCount();
0836:                if (count == 0)
0837:                    return s.length() > 0 ? -1 : 0;
0838:                for (int i = count - 1; i >= 0; i--) {
0839:                    Object element = getElementAt(i);
0840:                    if (compare(element, str))
0841:                        return i;
0842:                }
0843:                return -1;
0844:            }
0845:
0846:            /**
0847:             * This method is called when a key is typed or pressed.
0848:             *
0849:             * @param e the KeyEvent.
0850:             */
0851:            protected void keyTypedOrPressed(KeyEvent e) {
0852:                if (_searchableProvider != null
0853:                        && _searchableProvider.isPassive()) {
0854:                    _searchableProvider.processKeyEvent(e);
0855:                    return;
0856:                }
0857:
0858:                if (isActivateKey(e)) {
0859:                    String searchingText = "";
0860:                    if (e.getID() == KeyEvent.KEY_TYPED) {
0861:                        if (((e.getModifiers() & Toolkit.getDefaultToolkit()
0862:                                .getMenuShortcutKeyMask()) != 0)) { // if alt key is pressed
0863:                            return;
0864:                        }
0865:                        if (e.isAltDown()) {
0866:                            return;
0867:                        }
0868:
0869:                        searchingText = String.valueOf(e.getKeyChar());
0870:                    }
0871:                    showPopup(searchingText);
0872:                    if (e.getKeyCode() != KeyEvent.VK_ENTER) {
0873:                        e.consume();
0874:                    }
0875:                }
0876:            }
0877:
0878:            private int getIndex(int count, int index) {
0879:                return isReverseOrder() ? count - index - 1 : index;
0880:            }
0881:
0882:            /**
0883:             * Shows the search popup. By default, the search popup will be visible
0884:             * automatically when user types in the first key (in the case of JList, JTree, JTable)
0885:             * or types in designated keystroke (in the case of JTextComponent). So this method is only
0886:             * used when you want to show the popup manually.
0887:             *
0888:             * @param searchingText
0889:             */
0890:            public void showPopup(String searchingText) {
0891:                if (_searchableProvider == null) {
0892:                    fireSearchableEvent(new SearchableEvent(this ,
0893:                            SearchableEvent.SEARCHABLE_START, searchingText));
0894:                    showPopup(createSearchPopup(searchingText));
0895:                    _searchableProvider = new SearchableProvider() {
0896:                        public String getSearchingText() {
0897:                            return _popup != null ? _popup.getSearchingText()
0898:                                    : "";
0899:                        }
0900:
0901:                        public boolean isPassive() {
0902:                            return true;
0903:                        }
0904:
0905:                        public void processKeyEvent(KeyEvent e) {
0906:                            if (_popup != null) {
0907:                                _popup.processKeyEvent(e);
0908:                            }
0909:                        }
0910:                    };
0911:                }
0912:            }
0913:
0914:            /**
0915:             * Creates the popup to hold the searching text.
0916:             *
0917:             * @param searchingText
0918:             * @return the searching popup.
0919:             */
0920:            protected SearchPopup createSearchPopup(String searchingText) {
0921:                return new DefaultSearchPopup(searchingText);
0922:            }
0923:
0924:            /**
0925:             * Gets the searching text.
0926:             *
0927:             * @return the searching text.
0928:             */
0929:            public String getSearchingText() {
0930:                return _searchableProvider != null ? _searchableProvider
0931:                        .getSearchingText() : "";
0932:            }
0933:
0934:            private void showPopup(SearchPopup searchpopup) {
0935:                JRootPane rootPane = _component.getRootPane();
0936:                if (rootPane != null)
0937:                    _layeredPane = rootPane.getLayeredPane();
0938:                else {
0939:                    _layeredPane = null;
0940:                }
0941:
0942:                if (_layeredPane == null || isHeavyweightComponentEnabled()) {
0943:                    _popup = searchpopup;
0944:                    Point location = updateSizeAndLocation();
0945:                    if (location != null) {
0946:                        searchpopup.showPopup(location.x, location.y);
0947:                        _popup.setVisible(true);
0948:                    } else {
0949:                        _popup = null;
0950:                    }
0951:                } else {
0952:                    if (_popup != null && _layeredPane != null) {
0953:                        _layeredPane.remove(_popup);
0954:                        _layeredPane.validate();
0955:                        _layeredPane.repaint();
0956:                        _layeredPane = null;
0957:                    } else if (!_component.isShowing())
0958:                        _popup = null;
0959:                    else
0960:                        _popup = searchpopup;
0961:
0962:                    if (_popup == null || !_component.isDisplayable())
0963:                        return;
0964:
0965:                    if (_layeredPane == null) {
0966:                        System.err.println("Failed to find layeredPane.");
0967:                        return;
0968:                    }
0969:
0970:                    _layeredPane.add(_popup, JLayeredPane.POPUP_LAYER);
0971:
0972:                    updateSizeAndLocation();
0973:                    _popup.setVisible(true);
0974:                    _popup.validate();
0975:                }
0976:            }
0977:
0978:            private Point updateSizeAndLocation() {
0979:                Component component = getPopupLocationRelativeTo();
0980:                if (component == null) {
0981:                    component = JideSwingUtilities.getScrollPane(_component);
0982:                }
0983:                if (component == null) {
0984:                    component = _component;
0985:                }
0986:
0987:                Point componentLocation;
0988:                if (_popup != null) {
0989:                    Dimension size = _popup.getPreferredSize();
0990:                    switch (_popupLocation) {
0991:                    case SwingConstants.BOTTOM:
0992:                        try {
0993:                            componentLocation = component.getLocationOnScreen();
0994:                            componentLocation.y += component.getHeight();
0995:                        } catch (IllegalComponentStateException e) {
0996:                            return null; // can't get the location so just return.
0997:                        }
0998:                        if (!isHeavyweightComponentEnabled()) {
0999:                            SwingUtilities.convertPointFromScreen(
1000:                                    componentLocation, _layeredPane);
1001:                            if ((componentLocation.y + size.height > _layeredPane
1002:                                    .getHeight())) {
1003:                                componentLocation.y = _layeredPane.getHeight()
1004:                                        - size.height;
1005:                            }
1006:                        }
1007:                        break;
1008:                    case SwingConstants.TOP:
1009:                    default:
1010:                        try {
1011:                            componentLocation = component.getLocationOnScreen();
1012:                        } catch (IllegalComponentStateException e) {
1013:                            return null; // can't get the location so just return.
1014:                        }
1015:                        if (!isHeavyweightComponentEnabled()) {
1016:                            SwingUtilities.convertPointFromScreen(
1017:                                    componentLocation, _layeredPane);
1018:                        }
1019:                        componentLocation.y -= size.height;
1020:                        if ((componentLocation.y < 0)) {
1021:                            componentLocation.y = 0;
1022:                        }
1023:                        break;
1024:                    }
1025:                    if (!isHeavyweightComponentEnabled()) {
1026:                        _popup.setLocation(componentLocation);
1027:                        _popup.setSize(size);
1028:                    } else {
1029:                        _popup.packPopup();
1030:                    }
1031:                    return componentLocation;
1032:                } else {
1033:                    return null;
1034:                }
1035:            }
1036:
1037:            /**
1038:             * Checks if the key is used as a key to find the first occurence.
1039:             *
1040:             * @param e
1041:             * @return true if the key in KeyEvent is a key to find the first occurence. By default, home key is used.
1042:             */
1043:            protected boolean isFindFirstKey(KeyEvent e) {
1044:                return e.getKeyCode() == KeyEvent.VK_HOME;
1045:            }
1046:
1047:            /**
1048:             * Checks if the key is used as a key to find the last occurence.
1049:             *
1050:             * @param e
1051:             * @return true if the key in KeyEvent is a key to find the last occurence. By default, end key is used.
1052:             */
1053:            protected boolean isFindLastKey(KeyEvent e) {
1054:                return e.getKeyCode() == KeyEvent.VK_END;
1055:            }
1056:
1057:            /**
1058:             * Checks if the key is used as a key to find the previous occurence.
1059:             *
1060:             * @param e
1061:             * @return true if the key in KeyEvent is a key to find the previous occurence. By default, up arrow key is used.
1062:             */
1063:            protected boolean isFindPreviousKey(KeyEvent e) {
1064:                return e.getKeyCode() == KeyEvent.VK_UP;
1065:            }
1066:
1067:            /**
1068:             * Checks if the key is used as a key to find the next occurence.
1069:             *
1070:             * @param e
1071:             * @return true if the key in KeyEvent is a key to find the next occurence. By default, down arrow key is used.
1072:             */
1073:            protected boolean isFindNextKey(KeyEvent e) {
1074:                return e.getKeyCode() == KeyEvent.VK_DOWN;
1075:            }
1076:
1077:            /**
1078:             * Checks if the key is used as a navigation key. Navigation keys are keys which are used to
1079:             * navigate to other occurences of the searching string.
1080:             *
1081:             * @param e
1082:             * @return true if the key in KeyEvent is a navigation key.
1083:             */
1084:            protected boolean isNavigationKey(KeyEvent e) {
1085:                return isFindFirstKey(e) || isFindLastKey(e)
1086:                        || isFindNextKey(e) || isFindPreviousKey(e);
1087:            }
1088:
1089:            /**
1090:             * Checks if the key in KeyEvent should activate the search popup.
1091:             *
1092:             * @param e
1093:             * @return true if the keyChar is a letter or a digit or '*' or '?'.
1094:             */
1095:            protected boolean isActivateKey(KeyEvent e) {
1096:                char keyChar = e.getKeyChar();
1097:                return e.getID() == KeyEvent.KEY_TYPED
1098:                        && (Character.isLetterOrDigit(keyChar)
1099:                                || keyChar == '*' || keyChar == '?');
1100:            }
1101:
1102:            /**
1103:             * Checks if the key in KeyEvent should hide the search popup. If this method return true and
1104:             * the key is not used for navigation purpose ({@link #isNavigationKey(java.awt.event.KeyEvent)} return false), the popup will be hidden.
1105:             *
1106:             * @param e
1107:             * @return true if the keyCode in the KeyEvent is escape key, enter key,
1108:             *         or any of the arrow keys such as page up, page down, home, end, left, right, up and down.
1109:             */
1110:            protected boolean isDeactivateKey(KeyEvent e) {
1111:                int keyCode = e.getKeyCode();
1112:                return keyCode == KeyEvent.VK_ENTER
1113:                        || keyCode == KeyEvent.VK_ESCAPE
1114:                        || keyCode == KeyEvent.VK_PAGE_UP
1115:                        || keyCode == KeyEvent.VK_PAGE_DOWN
1116:                        || keyCode == KeyEvent.VK_HOME
1117:                        || keyCode == KeyEvent.VK_END
1118:                        || keyCode == KeyEvent.VK_LEFT
1119:                        || keyCode == KeyEvent.VK_RIGHT
1120:                        || keyCode == KeyEvent.VK_UP
1121:                        || keyCode == KeyEvent.VK_DOWN;
1122:            }
1123:
1124:            /**
1125:             * Checks if the key will trigger selecting all.
1126:             *
1127:             * @param e
1128:             * @return true if the key in KeyEvent is a key to trigger selecting all.
1129:             */
1130:            protected boolean isSelectAllKey(KeyEvent e) {
1131:                return ((e.getModifiers() & Toolkit.getDefaultToolkit()
1132:                        .getMenuShortcutKeyMask()) != 0)
1133:                        && e.getKeyCode() == KeyEvent.VK_A;
1134:            }
1135:
1136:            /**
1137:             * Checks if the key will trigger incremental selection.
1138:             *
1139:             * @param e
1140:             * @return true if the key in KeyEvent is a key to trigger incremental selection. By default, ctrl down key is used.
1141:             */
1142:            protected boolean isIncrementalSelectKey(KeyEvent e) {
1143:                return (e.getModifiers() & Toolkit.getDefaultToolkit()
1144:                        .getMenuShortcutKeyMask()) != 0;
1145:            }
1146:
1147:            /**
1148:             * Gets the foreground color when the searching text doesn't match with any of the elements in the component.
1149:             *
1150:             * @return the forground color for mismatch. If you never call
1151:             *         {@link #setMismatchForeground(java.awt.Color)}. red color will be used.
1152:             */
1153:            public Color getMismatchForeground() {
1154:                if (_mismatchForeground == null) {
1155:                    return Color.RED;
1156:                } else {
1157:                    return _mismatchForeground;
1158:                }
1159:            }
1160:
1161:            /**
1162:             * Sets the foreground for mismatch.
1163:             *
1164:             * @param mismatchForeground
1165:             */
1166:            public void setMismatchForeground(Color mismatchForeground) {
1167:                _mismatchForeground = mismatchForeground;
1168:            }
1169:
1170:            /**
1171:             * Checks if the case is sensitive during searching.
1172:             *
1173:             * @return true if the searching is case sensitive.
1174:             */
1175:            public boolean isCaseSensitive() {
1176:                return _caseSensitive;
1177:            }
1178:
1179:            /**
1180:             * Sets the case sensitive flag. By default, it's false meaning it's a case insensitive search.
1181:             *
1182:             * @param caseSensitive
1183:             */
1184:            public void setCaseSensitive(boolean caseSensitive) {
1185:                _caseSensitive = caseSensitive;
1186:            }
1187:
1188:            /**
1189:             * If it returns a positive number, it will wait for that many ms
1190:             * before doing the search. When the searching is complex, this
1191:             * flag will be useful to make the searching efficient. In the other words,
1192:             * if user types in several keys very quickly, there will be only one search.
1193:             * If it returns 0 or negative number, each key will generate a search.
1194:             *
1195:             * @return the number of ms delay before searching starts.
1196:             */
1197:            public int getSearchingDelay() {
1198:                return _searchingDelay;
1199:            }
1200:
1201:            /**
1202:             * If this flag is set to a positive number, it will wait for that many ms
1203:             * before doing the search. When the searching is complex, this
1204:             * flag will be useful to make the searching efficient. In the other words,
1205:             * if user types in several keys very quickly, there will be only one search.
1206:             * If this flag is set to 0 or a negative number, each key will generate a search with no delay.
1207:             *
1208:             * @param searchingDelay the number of ms delay before searching start.
1209:             */
1210:            public void setSearchingDelay(int searchingDelay) {
1211:                _searchingDelay = searchingDelay;
1212:            }
1213:
1214:            /**
1215:             * Checks if restart from the beginning when searching reaches the end
1216:             * or restart from the end when reaches beginning. Default is false.
1217:             *
1218:             * @return true or false.
1219:             */
1220:            public boolean isRepeats() {
1221:                return _repeats;
1222:            }
1223:
1224:            /**
1225:             * Sets the repeat flag. By default, it's false meaning it will stop searching
1226:             * when reaching the end or reaching the beginning.
1227:             *
1228:             * @param repeats
1229:             */
1230:            public void setRepeats(boolean repeats) {
1231:                _repeats = repeats;
1232:            }
1233:
1234:            /**
1235:             * Gets the foreground color used inn the search popup.
1236:             *
1237:             * @return the foreground. By default it will use the foreground of tooltip.
1238:             */
1239:            public Color getForeground() {
1240:                if (_foreground == null) {
1241:                    return UIDefaultsLookup.getColor("ToolTip.foreground");
1242:                } else {
1243:                    return _foreground;
1244:                }
1245:            }
1246:
1247:            /**
1248:             * Sets the foreground color used by popup.
1249:             *
1250:             * @param foreground
1251:             */
1252:            public void setForeground(Color foreground) {
1253:                _foreground = foreground;
1254:            }
1255:
1256:            /**
1257:             * Gets the background color used inn the search popup.
1258:             *
1259:             * @return the background. By default it will use the background of tooltip.
1260:             */
1261:            public Color getBackground() {
1262:                if (_background == null) {
1263:                    return UIDefaultsLookup.getColor("ToolTip.background");
1264:                } else {
1265:                    return _background;
1266:                }
1267:            }
1268:
1269:            /**
1270:             * Sets the background color used by popup.
1271:             *
1272:             * @param background
1273:             */
1274:            public void setBackground(Color background) {
1275:                _background = background;
1276:            }
1277:
1278:            /**
1279:             * Checks if it supports wildcard in searching text. By default it is true which means user can type
1280:             * in "*" or "?" to match with any charactors or any charactor. If it's false, it will treat "*" or "?"
1281:             * as a regular charactor.
1282:             *
1283:             * @return true if it supports wildcard.
1284:             */
1285:            public boolean isWildcardEnabled() {
1286:                return _wildcardEnabled;
1287:            }
1288:
1289:            /**
1290:             * Enable or disable the usage of wildcard.
1291:             *
1292:             * @param wildcardEnabled
1293:             * @see #isWildcardEnabled()
1294:             */
1295:            public void setWildcardEnabled(boolean wildcardEnabled) {
1296:                _wildcardEnabled = wildcardEnabled;
1297:            }
1298:
1299:            /**
1300:             * Gets the current text that appears in the search popup. By default it is "Search for: ".
1301:             *
1302:             * @return the text that appears in the search popup.
1303:             */
1304:            public String getSearchLabel() {
1305:                return _searchLabel;
1306:            }
1307:
1308:            /**
1309:             * Sets the text that appears in the search popup.
1310:             *
1311:             * @param searchLabel
1312:             */
1313:            public void setSearchLabel(String searchLabel) {
1314:                _searchLabel = searchLabel;
1315:            }
1316:
1317:            /**
1318:             * Adds the specified listener to receive searchable events from this
1319:             * searchable.
1320:             *
1321:             * @param l the searchable listener
1322:             */
1323:            public void addSearchableListener(SearchableListener l) {
1324:                listenerList.add(SearchableListener.class, l);
1325:            }
1326:
1327:            /**
1328:             * Removes the specified searchable listener so that it no longer
1329:             * receives searchable events.
1330:             *
1331:             * @param l the searchable listener
1332:             */
1333:            public void removeSearchableListener(SearchableListener l) {
1334:                listenerList.remove(SearchableListener.class, l);
1335:            }
1336:
1337:            /**
1338:             * Returns an array of all the <code>SearchableListener</code>s added
1339:             * to this <code>SearchableGroup</code> with
1340:             * <code>addSearchableListener</code>.
1341:             *
1342:             * @return all of the <code>SearchableListener</code>s added or an empty
1343:             *         array if no listeners have been added
1344:             * @see #addSearchableListener
1345:             */
1346:            public SearchableListener[] getSearchableListeners() {
1347:                return listenerList.getListeners(SearchableListener.class);
1348:            }
1349:
1350:            /**
1351:             * Fires a searchable event.
1352:             *
1353:             * @param e the event
1354:             */
1355:            protected void fireSearchableEvent(SearchableEvent e) {
1356:                Object[] listeners = listenerList.getListenerList();
1357:                for (int i = listeners.length - 2; i >= 0; i -= 2) {
1358:                    if (listeners[i] == SearchableListener.class) {
1359:                        ((SearchableListener) listeners[i + 1])
1360:                                .searchableEventFired(e);
1361:                    }
1362:                }
1363:            }
1364:
1365:            /**
1366:             * Gets the actual component which installed this Searchable.
1367:             *
1368:             * @return the actual component which installed this Searchable.
1369:             */
1370:            public Component getComponent() {
1371:                return _component;
1372:            }
1373:
1374:            /**
1375:             * Gets the popup location. It could be either {@link SwingConstants#TOP} or {@link SwingConstants#BOTTOM}.
1376:             *
1377:             * @return the popup location.
1378:             */
1379:            public int getPopupLocation() {
1380:                return _popupLocation;
1381:            }
1382:
1383:            /**
1384:             * Sets the popup location.
1385:             *
1386:             * @param popupLocation the popup location. The valid values are either {@link SwingConstants#TOP} or {@link SwingConstants#BOTTOM}.
1387:             */
1388:            public void setPopupLocation(int popupLocation) {
1389:                _popupLocation = popupLocation;
1390:            }
1391:
1392:            public abstract class SearchPopup extends JidePopup {
1393:                protected SearchField _textField;
1394:
1395:                @Override
1396:                public void processKeyEvent(KeyEvent e) {
1397:                    _textField.processKeyEvent(e);
1398:                    if (e.isConsumed()) {
1399:                        String text = getSearchingText();
1400:                        if (text.length() == 0) {
1401:                            return;
1402:                        }
1403:
1404:                        if (isSelectAllKey(e)) {
1405:                            selectAll(e, text);
1406:                            return;
1407:                        }
1408:
1409:                        int found;
1410:                        if (isFindPreviousKey(e)) {
1411:                            found = findPrevious(text);
1412:                            select(found, e, text);
1413:                        } else if (isFindNextKey(e)) {
1414:                            found = findNext(text);
1415:                            select(found, e, text);
1416:                        } else if (isFindFirstKey(e)) {
1417:                            found = findFirst(text);
1418:                            select(found, e, text);
1419:                        } else if (isFindLastKey(e)) {
1420:                            found = findLast(text);
1421:                            select(found, e, text);
1422:                        }
1423:                        //                else {
1424:                        //                    found = findFromCursor(text);
1425:                        //                }
1426:                    }
1427:                    if (e.getKeyCode() != KeyEvent.VK_ENTER) {
1428:                        e.consume();
1429:                    }
1430:                }
1431:
1432:                private void selectAll(KeyEvent e, String text) {
1433:                    boolean oldReverseOrder = isReverseOrder(); // keep the old reverse order and we will set it back.
1434:                    if (oldReverseOrder) {
1435:                        setReverseOrder(false);
1436:                    }
1437:
1438:                    int index = findFirst(text);
1439:                    if (index != -1) {
1440:                        setSelectedIndex(index, false); // clear side effect of ctrl-a will select all items
1441:                        _cursor = index; // as setSelectedIndex is used directly, we have to manually set the cursor value.
1442:                    }
1443:
1444:                    boolean oldRepeats = isRepeats(); // set repeats to false and set it back later.
1445:                    if (oldRepeats) {
1446:                        setRepeats(false);
1447:                    }
1448:
1449:                    while (index != -1) {
1450:                        int newIndex = findNext(text);
1451:                        if (index == newIndex) {
1452:                            index = -1;
1453:                        } else {
1454:                            index = newIndex;
1455:                        }
1456:                        if (index == -1) {
1457:                            break;
1458:                        }
1459:                        select(index, e, text);
1460:                    }
1461:
1462:                    if (oldRepeats) {
1463:                        setRepeats(oldRepeats);
1464:                    }
1465:
1466:                    if (oldReverseOrder) {
1467:                        setReverseOrder(oldReverseOrder);
1468:                    }
1469:                }
1470:
1471:                public String getSearchingText() {
1472:                    return _textField != null ? _textField.getText() : "";
1473:                }
1474:
1475:                abstract protected void select(int index, KeyEvent e,
1476:                        String searchingText);
1477:            }
1478:
1479:            /**
1480:             * Checks the searching order. By default the searchable starts searching from top to bottom. If this flag
1481:             * is false, it searchs from bottom to top.
1482:             *
1483:             * @return the reverseOrder flag.
1484:             */
1485:            public boolean isReverseOrder() {
1486:                return _reverseOrder;
1487:            }
1488:
1489:            /**
1490:             * Sets the searching order. By default the searchable starts searching from top to bottom. If this flag
1491:             * is false, it searchs from bottom to top.
1492:             *
1493:             * @param reverseOrder
1494:             */
1495:            public void setReverseOrder(boolean reverseOrder) {
1496:                _reverseOrder = reverseOrder;
1497:            }
1498:
1499:            /**
1500:             * Gets the localized string from resource bundle. Subclass can override it to provide its own string.
1501:             * Available keys are defined in swing.properties that begin with "Searchable.".
1502:             *
1503:             * @param key
1504:             * @return the localized string.
1505:             */
1506:            protected String getResourceString(String key) {
1507:                return Resource.getResourceBundle(Locale.getDefault())
1508:                        .getString(key);
1509:            }
1510:
1511:            /**
1512:             * Check if the searchable popup is visible.
1513:             *
1514:             * @return true if visible. Otherwise, false.
1515:             */
1516:            public boolean isPopupVisible() {
1517:                return _popup != null;
1518:            }
1519:
1520:            public boolean isHeavyweightComponentEnabled() {
1521:                return _heavyweightComponentEnabled;
1522:            }
1523:
1524:            public void setHeavyweightComponentEnabled(
1525:                    boolean heavyweightComponentEnabled) {
1526:                _heavyweightComponentEnabled = heavyweightComponentEnabled;
1527:            }
1528:
1529:            /**
1530:             * Gets the component that the location of the popup relative to.
1531:             *
1532:             * @return the component that the location of the popup relative to.
1533:             */
1534:            public Component getPopupLocationRelativeTo() {
1535:                return _popupLocationRelativeTo;
1536:            }
1537:
1538:            /**
1539:             * Sets the location of the popup relative to the specified component. Then based on the value of
1540:             * {@link #getPopupLocation()}. If you never set, we will use the searchable component or its scroll pane (if exists)
1541:             * as the popupLocationRelativeTo component.
1542:             *
1543:             * @param popupLocationRelativeTo
1544:             */
1545:            public void setPopupLocationRelativeTo(
1546:                    Component popupLocationRelativeTo) {
1547:                _popupLocationRelativeTo = popupLocationRelativeTo;
1548:            }
1549:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.