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-2016 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 */ 047public class FilterTextField extends JTextField 048{ 049 private static final long serialVersionUID = -2083433734204435457L; 050 private boolean displayClearIcon; 051 private ImageIcon clearIcon = Utilities.createImageIcon(IconPool.IMAGE_PATH+ 052 "/clear-filter.png"); 053 private ImageIcon clearIconPressed = 054 Utilities.createImageIcon(IconPool.IMAGE_PATH+ 055 "/clear-filter-down.png"); 056 private ImageIcon refreshIcon = 057 UIFactory.getImageIcon(UIFactory.IconType.WAIT_TINY); 058 059 private boolean mousePressed; 060 private boolean displayRefreshIcon; 061 062 /** The time during which the refresh icon is displayed by default. */ 063 public static long DEFAULT_REFRESH_ICON_TIME = 750; 064 065 private LinkedHashSet<ActionListener> listeners = new LinkedHashSet<>(); 066 private boolean constructorBorderSet; 067 068 /** Default constructor. */ 069 public FilterTextField() 070 { 071 super(15); 072 Border border = getBorder(); 073 if (border != null) 074 { 075 setBorder(BorderFactory.createCompoundBorder(border, new IconBorder())); 076 } 077 else 078 { 079 setBorder(new IconBorder()); 080 } 081 constructorBorderSet = true; 082 getDocument().addDocumentListener(new DocumentListener() 083 { 084 @Override 085 public void changedUpdate(DocumentEvent e) 086 { 087 insertUpdate(e); 088 } 089 090 @Override 091 public void insertUpdate(DocumentEvent e) 092 { 093 boolean displayIcon = getText().length() > 0; 094 if (FilterTextField.this.displayClearIcon != displayIcon) 095 { 096 FilterTextField.this.displayClearIcon = displayIcon; 097 repaint(); 098 } 099 } 100 @Override 101 public void removeUpdate(DocumentEvent e) 102 { 103 insertUpdate(e); 104 } 105 }); 106 107 addMouseListener(new MouseAdapter() 108 { 109 @Override 110 public void mousePressed(MouseEvent ev) 111 { 112 boolean p = getClearIconRectangle().contains(ev.getPoint()); 113 if (p != mousePressed) 114 { 115 mousePressed = p; 116 repaint(); 117 } 118 } 119 120 @Override 121 public void mouseReleased(MouseEvent ev) 122 { 123 if (mousePressed && getClearIconRectangle().contains(ev.getPoint())) 124 { 125 setText(""); 126 notifyListeners(); 127 } 128 mousePressed = false; 129 } 130 }); 131 } 132 133 @Override 134 public void addActionListener(ActionListener listener) 135 { 136 listeners.add(listener); 137 } 138 139 @Override 140 public void removeActionListener(ActionListener listener) 141 { 142 listeners.remove(listener); 143 } 144 145 @Override 146 public void setBorder(Border border) 147 { 148 if (constructorBorderSet && border != null) 149 { 150 border = BorderFactory.createCompoundBorder(border, new IconBorder()); 151 } 152 super.setBorder(border); 153 } 154 155 /** 156 * Displays a refresh icon on the text field (this is used for instance in 157 * the browsers that use this text field to specify a filter: the refresh 158 * icon is displayed to show that the filter is being displayed). 159 * @param display whether to display the refresh icon or not. 160 */ 161 public void displayRefreshIcon(boolean display) 162 { 163 if (display != displayRefreshIcon) 164 { 165 displayRefreshIcon = display; 166 repaint(); 167 } 168 } 169 170 /** 171 * Returns <CODE>true</CODE> if the refresh icon is displayed and 172 * <CODE>false</CODE> otherwise. 173 * @return <CODE>true</CODE> if the refresh icon is displayed and 174 * <CODE>false</CODE> otherwise. 175 */ 176 public boolean isRefreshIconDisplayed() 177 { 178 return displayRefreshIcon; 179 } 180 181 /** 182 * Displays a refresh icon on the text field (this is used for instance in 183 * the browsers that use this text field to specify a filter: the refresh 184 * icon is displayed to show that the filter is being displayed). 185 * @param time the time (in milliseconds) that the icon will be displayed. 186 */ 187 public void displayRefreshIcon(final long time) 188 { 189 displayRefreshIcon = true; 190 repaint(); 191 Thread t = new Thread(new Runnable() 192 { 193 @Override 194 public void run() 195 { 196 try 197 { 198 Thread.sleep(time); 199 } 200 catch (Throwable t) 201 { 202 } 203 finally 204 { 205 SwingUtilities.invokeLater(new Runnable() 206 { 207 @Override 208 public void run() 209 { 210 displayRefreshIcon = false; 211 repaint(); 212 } 213 }); 214 } 215 } 216 }); 217 t.start(); 218 } 219 220 private static int id = 1; 221 private void notifyListeners() 222 { 223 ActionEvent ev = new ActionEvent(this, id, 224 "CLEAR_FILTER"); 225 id ++; 226 for (ActionListener listener : listeners) 227 { 228 listener.actionPerformed(ev); 229 } 230 } 231 232 private Rectangle getClearIconRectangle() 233 { 234 ImageIcon icon = getClearIcon(); 235 int margin = getMargin(this, icon); 236 return new Rectangle(getWidth() - margin - icon.getIconWidth(), 237 margin, icon.getIconWidth(), icon.getIconHeight()); 238 } 239 240 /** The border of this filter text field. */ 241 private class IconBorder implements Border 242 { 243 @Override 244 public Insets getBorderInsets(Component c) 245 { 246 ImageIcon icon = getClearIcon(); 247 int rightInsets = 0; 248 if (displayClearIcon) 249 { 250 rightInsets += icon.getIconWidth() + getMargin(c, icon); 251 } 252 if (displayRefreshIcon) 253 { 254 rightInsets += refreshIcon.getIconWidth() + getMargin(c, refreshIcon); 255 } 256 return new Insets(0, 0, 0, rightInsets); 257 } 258 259 @Override 260 public void paintBorder(Component c, Graphics g, int x, int y, 261 int width, int height) 262 { 263 if (displayClearIcon || displayRefreshIcon) 264 { 265 Graphics2D g2d = (Graphics2D) g.create(); 266 int leftSpaceOfClearIcon = 0; 267 if (displayClearIcon) 268 { 269 ImageIcon icon = getClearIcon(); 270 int margin = (height - icon.getIconHeight()) / 2; 271 icon.paintIcon(c, 272 g2d, x + width - margin - icon.getIconWidth(), 273 y + margin); 274 leftSpaceOfClearIcon = margin + icon.getIconWidth(); 275 } 276 if (displayRefreshIcon) 277 { 278 int margin = (height - refreshIcon.getIconHeight()) / 2; 279 refreshIcon.paintIcon(c, g2d, x + width - margin - 280 refreshIcon.getIconWidth() - leftSpaceOfClearIcon, y + margin); 281 } 282 g2d.dispose(); //clean up 283 } 284 } 285 286 @Override 287 public boolean isBorderOpaque() 288 { 289 return false; 290 } 291 } 292 private int getMargin(Component c, ImageIcon icon) 293 { 294 return (c.getHeight() - icon.getIconHeight()) / 2; 295 } 296 297 private ImageIcon getClearIcon() 298 { 299 return mousePressed ? clearIconPressed : clearIcon; 300 } 301}