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 }