1 7 8 package org.jdesktop.swing.form; 9 10 import org.jdesktop.swing.data.MetaData; 11 import org.jdesktop.swing.data.NumberMetaData; 12 import org.jdesktop.swing.data.StringMetaData; 13 14 import org.jdesktop.swing.JXGlassBox; 15 16 import org.jdesktop.swing.binding.Binding; 17 18 import java.awt.AlphaComposite ; 19 import java.awt.Color ; 20 import java.awt.Component ; 21 import java.awt.Composite ; 22 import java.awt.Container ; 23 import java.awt.Font ; 24 import java.awt.Graphics ; 25 import java.awt.Graphics2D ; 26 import java.awt.GridBagConstraints ; 27 import java.awt.GridBagLayout ; 28 import java.awt.Insets ; 29 import java.awt.Point ; 30 import java.awt.Rectangle ; 31 import java.awt.event.ActionEvent ; 32 import java.awt.event.ActionListener ; 33 import java.awt.event.FocusEvent ; 34 import java.awt.event.FocusListener ; 35 import java.awt.event.MouseEvent ; 36 37 import java.beans.PropertyChangeEvent ; 38 import java.beans.PropertyChangeListener ; 39 40 import java.net.URL ; 41 42 import javax.swing.Box ; 43 import javax.swing.BoxLayout ; 44 import javax.swing.Icon ; 45 import javax.swing.ImageIcon ; 46 import javax.swing.JComponent ; 47 import javax.swing.JLabel ; 48 import javax.swing.JRootPane ; 49 import javax.swing.SwingConstants ; 50 import javax.swing.SwingUtilities ; 51 import javax.swing.Timer ; 52 import javax.swing.border.Border ; 53 import javax.swing.border.CompoundBorder ; 54 import javax.swing.border.EmptyBorder ; 55 import javax.swing.border.LineBorder ; 56 import javax.swing.event.MouseInputAdapter ; 57 58 62 63 public class BindingBorder 64 implements Border { 65 private static final int PAD = 0; 66 private static Icon defaultValidIcon; 67 private static Icon defaultInvalidIcon; 68 private static Icon defaultRequiredIcon; 69 70 private Binding binding; 71 private Icon validIcon; 72 private Icon invalidIcon; 73 private Icon requiredIcon; 74 private int iconPosition; 75 76 private FocusListener focusListener; 77 78 private Rectangle validIconBounds = new Rectangle (); 79 private Timer messageTriggerTimer; 80 private int messageTriggerX; 81 private int messageTriggerY; 82 private JXGlassBox messageBox; 83 84 static { 85 URL url = BindingBorder.class.getResource("resources/blue-tipicon.png"); 86 defaultValidIcon = new ImageIcon (url); 87 88 url = BindingBorder.class.getResource("resources/red-tipicon.png"); 89 defaultInvalidIcon = new ImageIcon (url); 90 91 url = BindingBorder.class.getResource("resources/asterisk.8x8.png"); 92 defaultRequiredIcon = new ImageIcon (url); 93 } 94 95 public BindingBorder(Binding binding) { 96 this(binding, SwingConstants.EAST); 97 } 98 99 public BindingBorder(Binding binding, int iconPosition) { 100 this(binding, defaultValidIcon, defaultInvalidIcon, iconPosition); 101 } 102 103 public BindingBorder(Binding binding, 104 Icon validIcon, Icon invalidIcon, int iconPosition) { 105 this(binding, validIcon, invalidIcon, null, iconPosition); 106 } 107 108 public BindingBorder(Binding binding, 109 Icon validIcon, Icon invalidIcon, 110 Icon requiredIcon, 111 int iconPosition) { 112 this.binding = binding; 113 this.validIcon = validIcon; 114 this.invalidIcon = invalidIcon; 115 this.iconPosition = iconPosition; 116 Component component = binding.getComponent(); 117 MetaData metaData = binding.getDataModel().getMetaData(binding.getFieldName()); 118 if (metaData.isRequired()) { 119 this.requiredIcon = requiredIcon; 120 } 121 binding.addPropertyChangeListener(new PropertyChangeListener () { 122 public void propertyChange(PropertyChangeEvent e) { 123 Binding binding = (Binding) e.getSource(); 124 if (e.getPropertyName().equals("validState")) { 125 binding.getComponent().repaint(); 126 } 127 } 128 }); 129 130 addFocusListener(component); 131 132 messageTriggerTimer = new Timer (1000, new MessageTrigger()); 133 MouseInputAdapter mouseAdapter = new MouseInputAdapter () { 134 public void mousePressed(MouseEvent e) { 135 int x = e.getX(); 136 int y = e.getY(); 137 if (!isMessageBoxShowing() && validIconBounds.contains(x, y)) { 138 if (messageTriggerTimer.isRunning()) { 139 messageTriggerTimer.stop(); 140 } 141 showMessageBox(x, y); 142 } 143 } 144 public void mouseMoved(MouseEvent e) { 145 if (!isMessageBoxShowing()) { 146 int x = e.getX(); 147 int y = e.getY(); 148 if (!messageTriggerTimer.isRunning()) { 149 if (validIconBounds.contains(x, y)) { 150 messageTriggerX = x; 151 messageTriggerY = y; 152 messageTriggerTimer.start(); 153 } 154 } 155 else { 156 if (!validIconBounds.contains(x, y)) { 157 messageTriggerTimer.stop(); 158 } 159 } 160 } 161 } 162 public void mouseExited(MouseEvent e) { 163 if (messageTriggerTimer.isRunning()) { 164 messageTriggerTimer.stop(); 165 } 166 } 167 }; 168 component.addMouseMotionListener(mouseAdapter); 169 component.addMouseListener(mouseAdapter); 170 } 171 172 public Insets getBorderInsets(Component c) { 173 if (requiredIcon != null) { 174 return new Insets (0, 0, 0, 175 validIcon.getIconWidth() + 176 requiredIcon.getIconWidth() + 177 (3 * PAD)); 178 } 179 return new Insets (0, 0, 0, validIcon.getIconWidth() + (2 * PAD)); 180 181 } 182 183 public boolean isBorderOpaque() { 184 return false; 185 } 186 187 public void paintBorder(Component c, Graphics g, int x, int y, 188 int width, int height) { 189 Graphics2D g2d = (Graphics2D ) g; 190 Composite oldComp = null; 191 boolean paintValid = binding.getValidState() != Binding.INVALID; 192 193 if (!hasFocus(c)) { 194 oldComp = g2d.getComposite(); 196 Composite alphaComp = AlphaComposite.getInstance(AlphaComposite. 197 SRC_OVER, 0.35f); 198 g2d.setComposite(alphaComp); 199 } 200 Icon validIcon = (paintValid ? this.validIcon : 201 this.invalidIcon); 202 203 int requiredIconSpace = (requiredIcon != null ? 204 requiredIcon.getIconWidth() + PAD : 0); 205 206 if (!isMessageBoxShowing()) { 207 validIconBounds.x = x + width - PAD - requiredIconSpace - 208 validIcon.getIconWidth(); 209 if (iconPosition == SwingConstants.NORTH_EAST) { 210 validIconBounds.y = y + PAD; 211 212 } else if (iconPosition == SwingUtilities.EAST){ validIconBounds.y = y + 214 ((height - validIcon.getIconHeight())/2); 215 } else if (iconPosition == SwingUtilities.WEST) { 216 validIconBounds.y = y + 217 ((height - validIcon.getIconHeight())/2); 218 validIconBounds.x = x + width - PAD - requiredIconSpace - 219 validIcon.getIconWidth(); 220 221 } 222 validIconBounds.width = validIcon.getIconWidth(); 223 validIconBounds.height = validIcon.getIconHeight(); 224 validIcon.paintIcon(c, g, validIconBounds.x, validIconBounds.y); 225 } 226 227 if (requiredIcon != null) { 228 int requiredX = x + width - PAD - requiredIcon.getIconWidth(); 229 int requiredY = y + (height - requiredIcon.getIconHeight()) / 2; 230 requiredIcon.paintIcon(c, g, requiredX, requiredY); 231 } 232 233 if (!paintValid) { 234 Color save = g.getColor(); 235 Color invalidColor = new Color (255, 0, 0, 225); 236 g.setColor(invalidColor); 237 g.drawRect(0, 0, c.getWidth(), c.getHeight()); 238 g.drawRect(1, 1, c.getWidth() - 2, c.getHeight() - 2); 239 g.setColor(save); 240 } 241 242 if (oldComp != null) { 243 g2d.setComposite(oldComp); 244 } 245 } 246 247 private void addFocusListener(Component component) { 248 if (focusListener == null) { 249 focusListener = new FocusListener () { 250 public void focusGained(FocusEvent e) { 251 binding.getComponent().repaint(); 252 } 253 public void focusLost(FocusEvent e) { 254 binding.getComponent().repaint(); 255 } 256 }; 257 } 258 component.addFocusListener(focusListener); 259 if (component instanceof Container ) { 262 Component children[] = ((Container )component).getComponents(); 263 for(int i = 0; i < children.length; i++) { 264 addFocusListener(children[i]); 265 } 266 } 267 } 268 269 private boolean hasFocus(Component component) { 270 if (component.isFocusOwner()) { 271 return true; 272 } 273 if (component instanceof Container ) { 274 Component children[] = ( (Container ) component).getComponents(); 275 for (int i = 0; i < children.length; i++) { 276 if (hasFocus(children[i])) { 277 return true; 278 } 279 } 280 } 281 return false; 282 } 283 284 private boolean isMessageBoxShowing() { 285 return (messageBox != null && messageBox.getParent() != null); 286 } 287 288 289 private void showMessageBox(int x, int y) { 290 JComponent component = binding.getComponent(); 291 boolean valid = binding.getValidState() != Binding.INVALID; 292 Color borderColor = valid ? Color.blue : Color.red; 293 Icon messageIcon = (valid ? validIcon : invalidIcon); 294 MetaData metaData = binding.getDataModel().getMetaData(binding. 295 getFieldName()); 296 297 messageBox = new JXGlassBox(); 298 messageBox.setAlpha(0.95f); 299 messageBox.setBorder(new LineBorder (borderColor, 1)); 300 GridBagLayout gridBag = new GridBagLayout (); 301 GridBagConstraints c = new GridBagConstraints (); 302 messageBox.setLayout(gridBag); 303 304 JLabel label = new JLabel (metaData.getLabel() + ":"); 305 Font boldFont = label.getFont().deriveFont(Font.BOLD); 306 label.setFont(boldFont); 307 c.anchor = GridBagConstraints.NORTHWEST; 308 c.insets = new Insets (2,4,0,2); 309 gridBag.setConstraints(label, c); 310 messageBox.add(label); 311 312 Box textContent = new Box(BoxLayout.X_AXIS); 313 c.gridx = 1; 314 c.anchor = GridBagConstraints.NORTH; 315 c.insets = new Insets (2, 2, 2, 2); 316 gridBag.setConstraints(textContent, c); 317 messageBox.add(textContent); 318 319 JLabel iconLabel = new JLabel (messageIcon); 320 c.gridx = 2; 321 c.anchor = GridBagConstraints.NORTHEAST; 322 c.insets = new Insets (1,0,0,1); 323 gridBag.setConstraints(iconLabel, c); 324 messageBox.add(iconLabel); 325 326 327 textContent.add(Box.createHorizontalStrut(4)); 328 Box content = new Box(BoxLayout.Y_AXIS); 329 textContent.add(content); 330 331 if (!valid) { 332 String errors[] = binding.getValidationErrors(); 333 if (errors.length > 0) { 334 for (int i = 0; i < errors.length; i++) { 335 content.add(new JLabel (errors[i])); 336 } 337 } 338 else { 339 content.add(new JLabel ("contains invalid value")); 340 } 341 } 342 else { 343 content.add(new JLabel ("value must be of type " + 344 metaData.getElementClass().getName())); 345 content.add(new JLabel ("value is " + 346 (metaData.isRequired() ? "required" : 347 "optional"))); 348 if (metaData instanceof NumberMetaData) { 349 NumberMetaData numberMetaData = (NumberMetaData) metaData; 350 Number minimum = numberMetaData.getMinimum(); 351 if (minimum != null) { 352 content.add(new JLabel ("minimum value is " + 353 minimum.toString())); 354 } 355 Number maximum = numberMetaData.getMaximum(); 356 if (maximum != null) { 357 content.add(new JLabel ("maximum value is " + 358 maximum.toString())); 359 } 360 } 361 362 } 363 368 Container glassPane = (Container ) component.getRootPane(). 369 getGlassPane(); 370 Component glassPaneChildren[] = glassPane.getComponents(); 373 for(int i = 0; i < glassPaneChildren.length; i++) { 374 glassPaneChildren[i].setVisible(false); 375 glassPane.remove(glassPaneChildren[i]); 376 } 377 messageBox.showOnGlassPane(glassPane, component, x, y, 378 SwingConstants.TOP); 379 380 } 381 382 private class MessageTrigger implements ActionListener { 383 public void actionPerformed(ActionEvent e) { 384 messageTriggerTimer.stop(); 385 showMessageBox(messageTriggerX, messageTriggerY); 386 } 387 } 388 } 389 | Popular Tags |