001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008 Sun Microsystems, Inc. 025 * Portions Copyright 2015 ForgeRock AS 026 */ 027 028package org.opends.guitools.controlpanel.ui.components; 029 030import java.awt.Component; 031import java.awt.Graphics; 032import java.awt.Graphics2D; 033import java.awt.Insets; 034import java.awt.Rectangle; 035import java.awt.event.ActionEvent; 036import java.awt.event.ActionListener; 037import java.awt.event.MouseAdapter; 038import java.awt.event.MouseEvent; 039import java.util.LinkedHashSet; 040 041import javax.swing.BorderFactory; 042import javax.swing.ImageIcon; 043import javax.swing.JTextField; 044import javax.swing.SwingUtilities; 045import javax.swing.border.Border; 046import javax.swing.event.DocumentEvent; 047import javax.swing.event.DocumentListener; 048 049import org.opends.guitools.controlpanel.browser.IconPool; 050import org.opends.guitools.controlpanel.util.Utilities; 051import org.opends.quicksetup.ui.UIFactory; 052 053/** 054 * A text field with an icon with 'X' shape on the right. When the user clicks 055 * on that icon, the contents of the text field are cleared. 056 * 057 */ 058public class FilterTextField extends JTextField 059{ 060 private static final long serialVersionUID = -2083433734204435457L; 061 private boolean displayClearIcon; 062 private ImageIcon clearIcon = Utilities.createImageIcon(IconPool.IMAGE_PATH+ 063 "/clear-filter.png"); 064 private ImageIcon clearIconPressed = 065 Utilities.createImageIcon(IconPool.IMAGE_PATH+ 066 "/clear-filter-down.png"); 067 private ImageIcon refreshIcon = 068 UIFactory.getImageIcon(UIFactory.IconType.WAIT_TINY); 069 070 private boolean mousePressed; 071 private boolean displayRefreshIcon; 072 073 /** 074 * The time during which the refresh icon is displayed by default. 075 */ 076 public static long DEFAULT_REFRESH_ICON_TIME = 750; 077 078 private LinkedHashSet<ActionListener> listeners = new LinkedHashSet<>(); 079 private boolean constructorBorderSet; 080 081 /** Default constructor. */ 082 public FilterTextField() 083 { 084 super(15); 085 Border border = getBorder(); 086 if (border != null) 087 { 088 setBorder(BorderFactory.createCompoundBorder(border, new IconBorder())); 089 } 090 else 091 { 092 setBorder(new IconBorder()); 093 } 094 constructorBorderSet = true; 095 getDocument().addDocumentListener(new DocumentListener() 096 { 097 /** {@inheritDoc} */ 098 public void changedUpdate(DocumentEvent e) 099 { 100 insertUpdate(e); 101 } 102 103 /** {@inheritDoc} */ 104 public void insertUpdate(DocumentEvent e) 105 { 106 boolean displayIcon = getText().length() > 0; 107 if (FilterTextField.this.displayClearIcon != displayIcon) 108 { 109 FilterTextField.this.displayClearIcon = displayIcon; 110 repaint(); 111 } 112 } 113 public void removeUpdate(DocumentEvent e) 114 { 115 insertUpdate(e); 116 } 117 }); 118 119 addMouseListener(new MouseAdapter() 120 { 121 /** {@inheritDoc} */ 122 public void mousePressed(MouseEvent ev) 123 { 124 boolean p = getClearIconRectangle().contains(ev.getPoint()); 125 if (p != mousePressed) 126 { 127 mousePressed = p; 128 repaint(); 129 } 130 } 131 132 /** {@inheritDoc} */ 133 public void mouseReleased(MouseEvent ev) 134 { 135 if (mousePressed && getClearIconRectangle().contains(ev.getPoint())) 136 { 137 setText(""); 138 notifyListeners(); 139 } 140 mousePressed = false; 141 } 142 }); 143 } 144 145 /** 146 * Adds an action listener to this text field. When the user clicks on the 147 * 'X' shaped icon the listeners are notified. 148 * @param listener the action listener. 149 */ 150 public void addActionListener(ActionListener listener) 151 { 152 listeners.add(listener); 153 } 154 155 /** 156 * Removes an action listener to this text field. 157 * @param listener the action listener. 158 */ 159 public void removeActionListener(ActionListener listener) 160 { 161 listeners.remove(listener); 162 } 163 164 /** {@inheritDoc} */ 165 public void setBorder(Border border) 166 { 167 if (constructorBorderSet && border != null) 168 { 169 border = BorderFactory.createCompoundBorder(border, new IconBorder()); 170 } 171 super.setBorder(border); 172 } 173 174 /** 175 * Displays a refresh icon on the text field (this is used for instance in 176 * the browsers that use this text field to specify a filter: the refresh 177 * icon is displayed to show that the filter is being displayed). 178 * @param display whether to display the refresh icon or not. 179 */ 180 public void displayRefreshIcon(boolean display) 181 { 182 if (display != displayRefreshIcon) 183 { 184 displayRefreshIcon = display; 185 repaint(); 186 } 187 } 188 189 /** 190 * Returns <CODE>true</CODE> if the refresh icon is displayed and 191 * <CODE>false</CODE> otherwise. 192 * @return <CODE>true</CODE> if the refresh icon is displayed and 193 * <CODE>false</CODE> otherwise. 194 */ 195 public boolean isRefreshIconDisplayed() 196 { 197 return displayRefreshIcon; 198 } 199 200 /** 201 * Displays a refresh icon on the text field (this is used for instance in 202 * the browsers that use this text field to specify a filter: the refresh 203 * icon is displayed to show that the filter is being displayed). 204 * @param time the time (in miliseconds) that the icon will be displayed. 205 * 206 */ 207 public void displayRefreshIcon(final long time) 208 { 209 displayRefreshIcon = true; 210 repaint(); 211 Thread t = new Thread(new Runnable() 212 { 213 public void run() 214 { 215 try 216 { 217 Thread.sleep(time); 218 } 219 catch (Throwable t) 220 { 221 } 222 finally 223 { 224 SwingUtilities.invokeLater(new Runnable() 225 { 226 public void run() 227 { 228 displayRefreshIcon = false; 229 repaint(); 230 } 231 }); 232 } 233 } 234 }); 235 t.start(); 236 } 237 238 private static int id = 1; 239 private void notifyListeners() 240 { 241 ActionEvent ev = new ActionEvent(this, id, 242 "CLEAR_FILTER"); 243 id ++; 244 for (ActionListener listener : listeners) 245 { 246 listener.actionPerformed(ev); 247 } 248 } 249 250 private Rectangle getClearIconRectangle() 251 { 252 ImageIcon icon = getClearIcon(); 253 int margin = getMargin(this, icon); 254 return new Rectangle(getWidth() - margin - icon.getIconWidth(), 255 margin, icon.getIconWidth(), icon.getIconHeight()); 256 } 257 258 /** 259 * The border of this filter text field. 260 * 261 */ 262 private class IconBorder implements Border 263 { 264 /** {@inheritDoc} */ 265 public Insets getBorderInsets(Component c) 266 { 267 ImageIcon icon = getClearIcon(); 268 int rightInsets = 0; 269 if (displayClearIcon) 270 { 271 rightInsets += icon.getIconWidth() + getMargin(c, icon); 272 } 273 if (displayRefreshIcon) 274 { 275 rightInsets += refreshIcon.getIconWidth() + getMargin(c, refreshIcon); 276 } 277 return new Insets(0, 0, 0, rightInsets); 278 } 279 280 /** {@inheritDoc} */ 281 public void paintBorder(Component c, Graphics g, int x, int y, 282 int width, int height) 283 { 284 if (displayClearIcon || displayRefreshIcon) 285 { 286 Graphics2D g2d = (Graphics2D) g.create(); 287 int leftSpaceOfClearIcon = 0; 288 if (displayClearIcon) 289 { 290 ImageIcon icon = getClearIcon(); 291 int margin = (height - icon.getIconHeight()) / 2; 292 icon.paintIcon(c, 293 g2d, x + width - margin - icon.getIconWidth(), 294 y + margin); 295 leftSpaceOfClearIcon = margin + icon.getIconWidth(); 296 } 297 if (displayRefreshIcon) 298 { 299 int margin = (height - refreshIcon.getIconHeight()) / 2; 300 refreshIcon.paintIcon(c, g2d, x + width - margin - 301 refreshIcon.getIconWidth() - leftSpaceOfClearIcon, y + margin); 302 } 303 g2d.dispose(); //clean up 304 } 305 } 306 307 /** {@inheritDoc} */ 308 public boolean isBorderOpaque() 309 { 310 return false; 311 } 312 } 313 private int getMargin(Component c, ImageIcon icon) 314 { 315 return (c.getHeight() - icon.getIconHeight()) / 2; 316 } 317 318 private ImageIcon getClearIcon() 319 { 320 return mousePressed ? clearIconPressed : clearIcon; 321 } 322}