001 /*
002 * $Id: JXFindPanel.java 3357 2009-05-30 04:20:07Z kschaefe $
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.Locale;
026 import java.util.regex.Pattern;
027
028 import javax.swing.Box;
029 import javax.swing.BoxLayout;
030 import javax.swing.JCheckBox;
031 import javax.swing.JLabel;
032 import javax.swing.JOptionPane;
033
034 import org.jdesktop.swingx.search.PatternModel;
035 import org.jdesktop.swingx.search.Searchable;
036
037 /**
038 * {@code JXFindPanel} is a basic find panel suitable for use in dialogs. It
039 * offers case-sensitivity, wrapped searching, and reverse searching.
040 *
041 * @author unascribed from JDNC
042 * @author Jeanette Winzenburg
043 */
044 public class JXFindPanel extends AbstractPatternPanel {
045
046 public static final String FIND_NEXT_ACTION_COMMAND = "findNext";
047 public static final String FIND_PREVIOUS_ACTION_COMMAND = "findPrevious";
048
049 protected Searchable searchable;
050
051 protected JCheckBox wrapCheck;
052 protected JCheckBox backCheck;
053 private boolean initialized;
054
055 /**
056 * Default constructor for the find panel. Constructs panel not targeted to
057 * any component.
058 */
059 public JXFindPanel() {
060 this(null);
061 }
062
063 /**
064 * Construct search panel targeted to specific <code>Searchable</code> component.
065 *
066 * @param searchible Component where search widget will try to locate and select
067 * information using methods of the <code>Searchible</code> interface.
068 */
069 public JXFindPanel(Searchable searchable) {
070 setName(getUIString(SEARCH_TITLE));
071 setSearchable(searchable);
072 initActions();
073 }
074
075 /**
076 * Sets the Searchable targeted of this find widget.
077 * Triggers a search with null pattern to release the old
078 * searchable, if any.
079 *
080 * @param searchable Component where search widget will try to locate and select
081 * information using methods of the {@link Searchable Searchable} interface.
082 */
083 public void setSearchable(Searchable searchable) {
084 if ((this.searchable != null) && this.searchable.equals(searchable)) return;
085 Searchable old = this.searchable;
086 if (old != null) {
087 old.search((Pattern) null);
088 }
089 this.searchable = searchable;
090 getPatternModel().setFoundIndex(-1);
091 firePropertyChange("searchable", old, this.searchable);
092 }
093
094 /**
095 * Notifies this component that it now has a parent component.
096 * When this method is invoked, the chain of parent components is
097 * set up with <code>KeyboardAction</code> event listeners.
098 */
099 @Override
100 public void addNotify() {
101 init();
102 super.addNotify();
103 }
104
105 /**
106 * Initializes component and its listeners and models.
107 */
108 protected void init() {
109 if (initialized) return;
110 initialized = true;
111 initComponents();
112 build();
113 bind();
114 }
115
116 //------------------ support synch the model <--> components
117
118
119 /**
120 * Configure and bind components to/from PatternModel.
121 */
122 @Override
123 protected void bind() {
124 super.bind();
125 getActionContainerFactory().configureButton(wrapCheck,
126 getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND),
127 null);
128 getActionContainerFactory().configureButton(backCheck,
129 getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND),
130 null);
131 }
132
133
134 /**
135 * called from listening to empty property of PatternModel.
136 *
137 * this implementation calls super and additionally synchs the
138 * enabled state of FIND_NEXT_ACTION_COMMAND, FIND_PREVIOUS_ACTION_COMMAND
139 * to !empty.
140 */
141 @Override
142 protected void refreshEmptyFromModel() {
143 super.refreshEmptyFromModel();
144 boolean enabled = !getPatternModel().isEmpty();
145 getAction(FIND_NEXT_ACTION_COMMAND).setEnabled(enabled);
146 getAction(FIND_PREVIOUS_ACTION_COMMAND).setEnabled(enabled);
147 }
148
149 //--------------------- action callbacks
150 /**
151 * Action callback for Find action.
152 * Find next/previous match using current setting of direction flag.
153 *
154 */
155 @Override
156 public void match() {
157 doFind();
158 }
159
160 /**
161 * Action callback for FindNext action.
162 * Sets direction flag to forward and calls find.
163 */
164 public void findNext() {
165 getPatternModel().setBackwards(false);
166 doFind();
167 }
168
169 /**
170 * Action callback for FindPrevious action.
171 * Sets direction flag to previous and calls find.
172 */
173 public void findPrevious() {
174 getPatternModel().setBackwards(true);
175 doFind();
176 }
177
178 /**
179 * Common standalone method to perform search. Used by the action callback methods
180 * for Find/FindNext/FindPrevious actions. Finds next/previous match using current
181 * setting of direction flag. Result is being reporred using showFoundMessage and
182 * showNotFoundMessage methods respectively.
183 *
184 * @see #match
185 * @see #findNext
186 * @see #findPrevious
187 */
188 protected void doFind() {
189 if (searchable == null)
190 return;
191 int foundIndex = doSearch();
192 boolean notFound = (foundIndex == -1) && !getPatternModel().isEmpty();
193 if (notFound) {
194 if (getPatternModel().isWrapping()) {
195 notFound = doSearch() == -1;
196 }
197 }
198 if (notFound) {
199 showNotFoundMessage();
200 } else {
201 showFoundMessage();
202 }
203 }
204
205 /**
206 * Performs search and returns index of the next match.
207 *
208 * @return Index of the next match in document.
209 */
210 protected int doSearch() {
211 int foundIndex = searchable.search(getPatternModel().getPattern(),
212 getPatternModel().getFoundIndex(), getPatternModel().isBackwards());
213 getPatternModel().setFoundIndex(foundIndex);
214 return getPatternModel().getFoundIndex();
215 // first try on #236-swingx - foundIndex wrong in backwards search.
216 // re-think: autoIncrement in PatternModel?
217 // return foundIndex;
218 }
219
220 /**
221 * Report that suitable match is found.
222 */
223 protected void showFoundMessage() {
224
225 }
226
227 /**
228 * Report that no match is found.
229 */
230 protected void showNotFoundMessage() {
231 JOptionPane.showMessageDialog(this, getUIString("notFound"));
232 }
233
234
235 //-------------- dynamic Locale support
236
237
238
239 @Override
240 protected void updateLocaleState(Locale locale) {
241 super.updateLocaleState(locale);
242 setName(getUIString(SEARCH_TITLE, locale));
243 }
244
245 //-------------------------- initial
246
247
248 /**
249 * creates and registers all "executable" actions.
250 * Meaning: the actions bound to a callback method on this.
251 */
252 @Override
253 protected void initExecutables() {
254 getActionMap().put(FIND_NEXT_ACTION_COMMAND,
255 createBoundAction(FIND_NEXT_ACTION_COMMAND, "findNext"));
256 getActionMap().put(FIND_PREVIOUS_ACTION_COMMAND,
257 createBoundAction(FIND_PREVIOUS_ACTION_COMMAND, "findPrevious"));
258 super.initExecutables();
259 }
260
261
262
263 //----------------------------- init ui
264
265 /**
266 * Create and initialize components.
267 */
268 @Override
269 protected void initComponents() {
270 super.initComponents();
271 wrapCheck = new JCheckBox();
272 backCheck = new JCheckBox();
273 }
274
275
276
277 /**
278 * Compose and layout all the subcomponents.
279 */
280 protected void build() {
281 Box lBox = new Box(BoxLayout.LINE_AXIS);
282 lBox.add(searchLabel);
283 lBox.add(new JLabel(":"));
284 lBox.add(new JLabel(" "));
285 lBox.setAlignmentY(Component.TOP_ALIGNMENT);
286 Box rBox = new Box(BoxLayout.PAGE_AXIS);
287 rBox.add(searchField);
288 rBox.add(matchCheck);
289 rBox.add(wrapCheck);
290 rBox.add(backCheck);
291 rBox.setAlignmentY(Component.TOP_ALIGNMENT);
292
293 setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
294
295 add(lBox);
296 add(rBox);
297 }
298
299 }