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 }