001    /*
002     * $Id: JXFindPanel.java,v 1.13 2006/01/17 12:52:08 kleopatra Exp $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx;
023    
024    import java.awt.Component;
025    import java.util.regex.Pattern;
026    
027    import javax.swing.Box;
028    import javax.swing.BoxLayout;
029    import javax.swing.JCheckBox;
030    import javax.swing.JLabel;
031    import javax.swing.JOptionPane;
032    import javax.swing.UIManager;
033    
034    /**
035     * Find panel to incorporate search capability into the users application.
036     * The most intended usage is to adding this panel to the dialogs.
037     * 
038     * 
039     * @author ??
040     * @author Jeanette Winzenburg
041     */
042    public class JXFindPanel extends AbstractPatternPanel {
043    
044        public static final String FIND_NEXT_ACTION_COMMAND = "findNext";
045        public static final String FIND_PREVIOUS_ACTION_COMMAND = "findPrevious";
046        
047        protected Searchable searchable;
048    
049        protected JCheckBox wrapCheck;
050        protected JCheckBox backCheck;
051        private boolean initialized;
052    
053        /*
054         * Default constructor for the find panel. Constructs panel not targeted to
055         * any component.
056         */
057        public JXFindPanel() {
058            this(null);
059        }
060        
061        /*
062         * Construct search panel targeted to specific <code>Searchable</code> component.
063         *
064         * @param searchible Component where search widget will try to locate and select
065         *                   information using methods of the <code>Searchible</code> interface.
066         */
067        public JXFindPanel(Searchable searchable) {
068            setSearchable(searchable);
069            initActions();
070        }
071        
072        /**
073         * Sets the Searchable targeted of this find widget.
074         * Triggers a search with null pattern to release the old
075         * searchable, if any.
076         * 
077         * @param searchable Component where search widget will try to locate and select
078         *                   information using methods of the {@link Searchable Searchable} interface.
079         */
080        public void setSearchable(Searchable searchable) {
081            if ((this.searchable != null) && this.searchable.equals(searchable)) return;
082            Searchable old = this.searchable;
083            if (old != null) {
084                old.search((Pattern) null);
085            }
086            this.searchable = searchable;
087            getPatternModel().setFoundIndex(-1);
088            firePropertyChange("searchable", old, this.searchable);
089        }
090        
091        /**
092         * Notifies this component that it now has a parent component.
093         * When this method is invoked, the chain of parent components is
094         * set up with <code>KeyboardAction</code> event listeners.
095         */
096        public void addNotify() {
097            init();
098            super.addNotify();
099        }
100        
101       /**
102        * Initializes component and its listeners and models.
103        */ 
104        protected void init() {
105            if (initialized) return;
106            initialized = true;
107            initComponents();
108            build();
109            bind();
110            setName(getUIString(SEARCH_TITLE));
111        }
112        
113        //------------------ support synch the model <--> components
114        
115    
116        /**
117         * Configure and bind components to/from PatternModel.
118         */
119        @Override
120        protected void bind() {
121            super.bind();
122            getActionContainerFactory().configureButton(wrapCheck, 
123                    getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND),
124                    null);
125            getActionContainerFactory().configureButton(backCheck, 
126                    getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND),
127                    null);
128        }
129    
130        
131        /**
132         * called from listening to empty property of PatternModel.
133         * 
134         * this implementation calls super and additionally synchs the 
135         * enabled state of FIND_NEXT_ACTION_COMMAND, FIND_PREVIOUS_ACTION_COMMAND
136         * to !empty.
137         */
138        @Override
139        protected void refreshEmptyFromModel() {
140            super.refreshEmptyFromModel();
141            boolean enabled = !getPatternModel().isEmpty();
142            getAction(FIND_NEXT_ACTION_COMMAND).setEnabled(enabled);
143            getAction(FIND_PREVIOUS_ACTION_COMMAND).setEnabled(enabled);
144        }
145    
146        //--------------------- action callbacks
147        /**
148         * Action callback for Find action.
149         * Find next/previous match using current setting of direction flag.
150         * 
151         */
152        public void match() {
153            doFind();
154        }
155    
156        /**
157         * Action callback for FindNext action.
158         * Sets direction flag to forward and calls find.
159         */
160        public void findNext() {
161            getPatternModel().setBackwards(false);
162            doFind();
163        }
164        
165        /**
166         * Action callback for FindPrevious action.
167         * Sets direction flag to previous and calls find.
168         */
169        public void findPrevious() {
170            getPatternModel().setBackwards(true);
171            doFind();
172        }
173        
174        /**
175         * Common standalone method to perform search. Used by the action callback methods 
176         * for Find/FindNext/FindPrevious actions. Finds next/previous match using current 
177         * setting of direction flag. Result is being reporred using showFoundMessage and 
178         * showNotFoundMessage methods respectively.
179         *
180         * @see #match
181         * @see #findNext
182         * @see #findPrevious
183         */
184        protected void doFind() {
185            if (searchable == null)
186                return;
187            int foundIndex = doSearch();
188            boolean notFound = (foundIndex == -1) && !getPatternModel().isEmpty();
189            if (notFound) {
190                if (getPatternModel().isWrapping()) {
191                    notFound = doSearch() == -1;
192                }
193            }
194            if (notFound) {
195                showNotFoundMessage();
196            } else {
197                showFoundMessage();
198            }
199        }
200    
201        /**
202         * Proforms search and returns index of the next match.
203         *
204         * @return Index of the next match in document.
205         */
206        protected int doSearch() {
207            int foundIndex = searchable.search(getPatternModel().getPattern(), 
208                    getPatternModel().getFoundIndex(), getPatternModel().isBackwards());
209            getPatternModel().setFoundIndex(foundIndex);
210            return getPatternModel().getFoundIndex();
211    //         first try on #236-swingx - foundIndex wrong in backwards search.
212    //         re-think: autoIncrement in PatternModel? 
213    //        return foundIndex; 
214        }
215    
216        /**
217         * Report that suitable match is found.
218         */
219        protected void showFoundMessage() {
220            
221        }
222    
223        /**
224         * Report that no match is found.
225         */
226        protected void showNotFoundMessage() {
227            JOptionPane.showMessageDialog(this, UIManager.getString("valueNotFound"));
228        }
229    
230    
231        //-------------------------- initial
232        
233        /**
234         * creates and registers all "executable" actions.
235         * Meaning: the actions bound to a callback method on this.
236         */
237        @Override
238        protected void initExecutables() {
239            getActionMap().put(FIND_NEXT_ACTION_COMMAND, 
240                    createBoundAction(FIND_NEXT_ACTION_COMMAND, "findNext"));
241            getActionMap().put(FIND_PREVIOUS_ACTION_COMMAND, 
242                    createBoundAction(FIND_PREVIOUS_ACTION_COMMAND, "findPrevious"));
243            super.initExecutables();
244        }
245    
246    
247      
248    //----------------------------- init ui
249        
250        /** 
251         * Create and initialize components.
252         */
253        protected void initComponents() {
254            super.initComponents();
255            wrapCheck = new JCheckBox();
256            backCheck = new JCheckBox();
257        }
258    
259    
260    
261        /**
262         * Compose and layout all the subcomponents.
263         */
264        protected void build() {
265            Box lBox = new Box(BoxLayout.LINE_AXIS); 
266            lBox.add(searchLabel);
267            lBox.add(new JLabel(":"));
268            lBox.add(new JLabel("  "));
269            lBox.setAlignmentY(Component.TOP_ALIGNMENT);
270            Box rBox = new Box(BoxLayout.PAGE_AXIS); 
271            rBox.add(searchField);
272            rBox.add(matchCheck);
273            rBox.add(wrapCheck);
274            rBox.add(backCheck);
275            rBox.setAlignmentY(Component.TOP_ALIGNMENT);
276    
277            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
278            
279            add(lBox);
280            add(rBox);
281        }
282    }