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