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