Source Code Cross Referenced for IconFeedbackPanel.java in  » Swing-Library » jgoodies-validation » com » jgoodies » validation » tutorial » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » jgoodies validation » com.jgoodies.validation.tutorial.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright (c) 2003-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003:         *
004:         * Redistribution and use in source and binary forms, with or without
005:         * modification, are permitted provided that the following conditions are met:
006:         *
007:         *  o Redistributions of source code must retain the above copyright notice,
008:         *    this list of conditions and the following disclaimer.
009:         *
010:         *  o Redistributions in binary form must reproduce the above copyright notice,
011:         *    this list of conditions and the following disclaimer in the documentation
012:         *    and/or other materials provided with the distribution.
013:         *
014:         *  o Neither the name of JGoodies Karsten Lentzsch nor the names of
015:         *    its contributors may be used to endorse or promote products derived
016:         *    from this software without specific prior written permission.
017:         *
018:         * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019:         * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020:         * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021:         * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025:         * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026:         * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027:         * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028:         * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029:         */
030:
031:        package com.jgoodies.validation.tutorial.util;
032:
033:        import java.awt.*;
034:        import java.beans.PropertyChangeEvent;
035:        import java.beans.PropertyChangeListener;
036:        import java.util.Map;
037:
038:        import javax.swing.*;
039:        import javax.swing.text.JTextComponent;
040:
041:        import com.jgoodies.validation.ValidationMessage;
042:        import com.jgoodies.validation.ValidationResult;
043:        import com.jgoodies.validation.ValidationResultModel;
044:        import com.jgoodies.validation.view.ValidationComponentUtils;
045:        import com.jgoodies.validation.view.ValidationResultViewFactory;
046:
047:        /**
048:         * Can display validation feedback icons "over" a content panel.
049:         * It observes a ValidationResultModel and creates icon labels
050:         * in a feedback layer of a {@link JLayeredPane} on top of the content layer.
051:         * To position the feedback labels, the content pane is traversed
052:         * and searched for text components that match a validation message key
053:         * in this panel's observed ValidationResultModel.<p>
054:         *
055:         * <strong>Note:</strong> This panel doesn't reserve space for the portion
056:         * used to display the overlaid feedback components. It has been designed
057:         * to not change the layout of the wrapped content. Therefore you must reserve
058:         * this space, or in other words, you must ensure that the wrapped content
059:         * provides enough space to display the overlaid components.
060:         * Since the current implementation positions the overlay components
061:         * in the lower left, just make sure that there are about 6 pixel to the left
062:         * and bottom of the input components that can be marked.<p>
063:         *
064:         * This panel handles two event types: <ol>
065:         * <li>the ValidationResultModel changes; in this case the set of visible
066:         * feedback components shall mark the input components that match the
067:         * new validation result. This is done by this class' internal
068:         * <code>ValidationResultChangeHandler</code> which in turn invokes
069:         * <code>#updateFeedbackComponents</code>.
070:         * <li>the content layout changes; the feedback components must then be
071:         * repositioned to reflect the position of the overlaid input components.
072:         * This is done by overriding <code>#validateTree</code> and invoking
073:         * <code>#repositionFeedBackComponents</code> after the child tree has
074:         * been laid out. The current simple but expensive implementation
075:         * updates all components.
076:         * </ol><p>
077:         *
078:         * TODO: Check how the wrapping mechanism shall work with
079:         * JSplitPanes, JTabbedPanes and CardPanels. At least provide
080:         * guidelines, how to wrap these panel types, or how to handle
081:         * these cases.<p>
082:         *
083:         * TODO: Turn this class into an abstract superclass.
084:         * Subclasses shall implement the feedback component creation
085:         * and specify where to locate the feedback component relative
086:         * to the underlying content component.<p>
087:         *
088:         * TODO: Consider adding a mechanism, so that components can be added
089:         * and removed later.
090:         *
091:         * @author Karsten Lentzsch
092:         * @version $Revision: 1.21 $
093:         */
094:        public final class IconFeedbackPanel extends JLayeredPane {
095:
096:            private static final int CONTENT_LAYER = 1;
097:            private static final int FEEDBACK_LAYER = 2;
098:
099:            /**
100:             * Holds the ValidationResult and reports changes in that result.
101:             * Used to update the state of the feedback components.
102:             */
103:            private final ValidationResultModel model;
104:
105:            /**
106:             * Refers to the content panel that holds the content components.
107:             */
108:            private final JComponent content;
109:
110:            // Instance Creation ******************************************************
111:
112:            /**
113:             * Creates an IconFeedbackPanel on the given ValidationResultModel
114:             * using the specified content panel.<p>
115:             *
116:             * <strong>Note:</strong> Typically you should wrap component trees with
117:             * {@link #getWrappedComponentTree(ValidationResultModel, JComponent)},
118:             * not this constructor.<p>
119:             *
120:             * <strong>Note:</strong> You must not add or remove components
121:             * from the content once this constructor has been invoked.
122:             *
123:             * @param model      the ValidationResultModel to observe
124:             * @param content    the panel that contains the content components
125:             *
126:             * @throws NullPointerException if model or content is <code>null</code>.
127:             */
128:            public IconFeedbackPanel(ValidationResultModel model,
129:                    JComponent content) {
130:                if (model == null)
131:                    throw new NullPointerException(
132:                            "The validation result model must not be null.");
133:                if (content == null)
134:                    throw new NullPointerException(
135:                            "The content must not be null.");
136:
137:                this .model = model;
138:                this .content = content;
139:                setLayout(new SimpleLayout());
140:                add(content, CONTENT_LAYER);
141:                initEventHandling();
142:            }
143:
144:            // Convenience Code *******************************************************
145:
146:            /**
147:             * Wraps the components in the given component tree with instances
148:             * of IconFeedbackPanel where necessary. Such a wrapper is required
149:             * for all JScrollPanes that contain multiple children and
150:             * for the root - unless it's a JScrollPane with multiple children.
151:             *
152:             * @param root    the root of the component tree to wrap
153:             * @return the wrapped component tree
154:             */
155:            public static JComponent getWrappedComponentTree(
156:                    ValidationResultModel model, JComponent root) {
157:                wrapComponentTree(model, root);
158:                return isScrollPaneWithUnmarkableView(root) ? root
159:                        : new IconFeedbackPanel(model, root);
160:            }
161:
162:            private static void wrapComponentTree(ValidationResultModel model,
163:                    Container container) {
164:                if (!(container instanceof  JScrollPane)) {
165:                    int componentCount = container.getComponentCount();
166:                    for (int i = 0; i < componentCount; i++) {
167:                        Component child = container.getComponent(i);
168:                        if (child instanceof  Container)
169:                            wrapComponentTree(model, (Container) child);
170:                    }
171:                    return;
172:                }
173:                JScrollPane scrollPane = (JScrollPane) container;
174:                JViewport viewport = scrollPane.getViewport();
175:                JComponent view = (JComponent) viewport.getView();
176:                if (isMarkable(view))
177:                    return;
178:                // TODO: Consider adding the following sanity check:
179:                // the view must not be an IconFeedbackPanel
180:                Component wrappedView = new IconFeedbackPanel(model, view);
181:                viewport.setView(wrappedView);
182:                wrapComponentTree(model, view);
183:            }
184:
185:            private static boolean isScrollPaneWithUnmarkableView(Component c) {
186:                if (!(c instanceof  JScrollPane))
187:                    return false;
188:                JScrollPane scrollPane = (JScrollPane) c;
189:                JViewport viewport = scrollPane.getViewport();
190:                JComponent view = (JComponent) viewport.getView();
191:                return !isMarkable(view);
192:            }
193:
194:            // Initialization *********************************************************
195:
196:            /**
197:             * Registers a listener with the validation result model that updates
198:             * the feedback components.
199:             */
200:            private void initEventHandling() {
201:                model.addPropertyChangeListener(
202:                        ValidationResultModel.PROPERTYNAME_RESULT,
203:                        new ValidationResultChangeHandler());
204:            }
205:
206:            // Abstract Behavior ******************************************************
207:
208:            /**
209:             * Creates and returns a validation feedback component
210:             * that shall overlay the specified content component.<p>
211:             *
212:             * This implementation returns a JLabel. The validation result's severity
213:             * is used to lookup the label's icon; the result's message text is set
214:             * as the label's tooltip text.<p>
215:             *
216:             * TODO: Turn this method into an abstract method if this class
217:             * becomes an abstract superclass of general feedback overlay panels.
218:             *
219:             * @param result            determines the label's icon and tooltip text
220:             * @param contentComponent  the component to get overlaid feedback
221:             * @return the feedback component that overlays the content component
222:             *
223:             * @throws NullPointerException if the result is <code>null</code>
224:             */
225:            private JComponent createFeedbackComponent(ValidationResult result,
226:                    Component contentComponent) {
227:                Icon icon = ValidationResultViewFactory.getSmallIcon(result
228:                        .getSeverity());
229:                JLabel label = new JLabel(icon);
230:                label.setToolTipText(getMessagesToolTipText(result));
231:                label.setSize(label.getPreferredSize());
232:                return label;
233:            }
234:
235:            /**
236:             * Returns a string representation of the given validation result,
237:             * intended for tool tips. Unlike {@link ValidationResult#getMessagesText()}
238:             * this method returns an HTML string. It is invoked by
239:             * {@link #createFeedbackComponent(ValidationResult, Component)}
240:             * and the result won't be empty.
241:             *
242:             * @param result provides the ValidationMessages to iterate over
243:             * @return an HTML representation of the given result
244:             *
245:             * @since 1.3.1
246:             */
247:            private static String getMessagesToolTipText(ValidationResult result) {
248:                StringBuilder builder = new StringBuilder("<html>");
249:                for (ValidationMessage message : result.getMessages()) {
250:                    if (builder.length() > 0) {
251:                        builder.append("<br>");
252:                    }
253:                    builder.append(message.formattedText());
254:                }
255:                builder.append("</html>");
256:                return builder.toString();
257:            }
258:
259:            /**
260:             * Computes and returns the origin of the given feedback component
261:             * using the content component's origin.<p>
262:             *
263:             * This implementation returns a JLabel. The validation result's severity
264:             * is used to lookup the label's icon; the result's message text is
265:             * set as the label's tooltip text.<p>
266:             *
267:             * TODO: Turn this method into an abstract method if this class
268:             * becomes an abstract superclass of general feedback overlay panels.
269:             *
270:             * @param feedbackComponent  the component that overlays the content
271:             * @param contentComponent   the component to get overlaid feedback
272:             * @return the feedback component's origin
273:             *
274:             * @throws NullPointerException if the feedback component or content component
275:             *     is <code>null</code>
276:             */
277:            private Point getFeedbackComponentOrigin(
278:                    JComponent feedbackComponent, Component contentComponent) {
279:                boolean isLTR = contentComponent.getComponentOrientation()
280:                        .isLeftToRight();
281:                int x = contentComponent.getX()
282:                        + (isLTR ? 0 : contentComponent.getWidth() - 1)
283:                        - feedbackComponent.getWidth() / 2;
284:                int y = contentComponent.getY() + contentComponent.getHeight()
285:                        - feedbackComponent.getHeight() + 2;
286:
287:                return new Point(x, y);
288:            }
289:
290:            // Updating the Overlay Components ****************************************
291:
292:            private void removeAllFeedbackComponents() {
293:                int componentCount = getComponentCount();
294:                for (int i = componentCount - 1; i >= 0; i--) {
295:                    Component child = getComponent(i);
296:                    int layer = getLayer(child);
297:                    if (layer == FEEDBACK_LAYER)
298:                        remove(i);
299:                }
300:            }
301:
302:            /**
303:             * Traverses the component tree starting at the given container
304:             * and creates a feedback component for each JTextComponent that
305:             * is associated with a message in the specified <code>keyMap</code>.<p>
306:             *
307:             * The arguments passed to the feedback component creation method
308:             * are the visited component and its associated validation sub result.
309:             * This sub result is requested from the specified <code>keyMap</code>
310:             * using the visited component's message key.
311:             *
312:             * @param container   the component tree root
313:             * @param keyMap      maps messages keys to associated validation results
314:             */
315:            private void visitComponentTree(Container container,
316:                    Map<Object, ValidationResult> keyMap, int xOffset,
317:                    int yOffset) {
318:                int componentCount = container.getComponentCount();
319:                for (int i = 0; i < componentCount; i++) {
320:                    Component child = container.getComponent(i);
321:                    if (!child.isVisible())
322:                        continue;
323:                    if (isMarkable(child)) {
324:                        if (isScrollPaneView(child)) {
325:                            Component containerParent = container.getParent();
326:                            addFeedbackComponent(containerParent,
327:                                    (JComponent) child, keyMap, xOffset
328:                                            - containerParent.getX(), yOffset
329:                                            - containerParent.getY());
330:                        } else {
331:                            addFeedbackComponent(child, (JComponent) child,
332:                                    keyMap, xOffset, yOffset);
333:                        }
334:                    } else if (isScrollPaneView(child)) {
335:                        // Just do nothing.
336:                    } else if (child instanceof  Container) {
337:                        visitComponentTree((Container) child, keyMap, xOffset
338:                                + child.getX(), yOffset + child.getY());
339:                    }
340:                }
341:            }
342:
343:            private static boolean isScrollPaneView(Component c) {
344:                Container container = c.getParent();
345:                Container containerParent = container.getParent();
346:                return (container instanceof  JViewport)
347:                        && (containerParent instanceof  JScrollPane);
348:            }
349:
350:            /**
351:             * Checks and answers if the given component can be marked or not.<p>
352:             *
353:             * TODO: Check the combobox editable state.<p>
354:             *
355:             * TODO: Add the JSpinner to the list of markable components.
356:             *
357:             * @param component  the component to be checked
358:             * @return true if the given component can be marked, false if not
359:             */
360:            private static boolean isMarkable(Component component) {
361:                return component instanceof  JTextComponent
362:                        || component instanceof  JComboBox;
363:            }
364:
365:            private void addFeedbackComponent(Component contentComponent,
366:                    JComponent messageComponent,
367:                    Map<Object, ValidationResult> keyMap, int xOffset,
368:                    int yOffset) {
369:                ValidationResult result = getAssociatedResult(messageComponent,
370:                        keyMap);
371:                JComponent feedbackComponent = createFeedbackComponent(result,
372:                        contentComponent);
373:                if (feedbackComponent == null)
374:                    return;
375:                add(feedbackComponent, Integer.valueOf(FEEDBACK_LAYER));
376:                Point overlayPosition = getFeedbackComponentOrigin(
377:                        feedbackComponent, contentComponent);
378:                overlayPosition.translate(xOffset, yOffset);
379:                feedbackComponent.setLocation(overlayPosition);
380:            }
381:
382:            /**
383:             * Returns the ValidationResult associated with the given component
384:             * using the specified validation result key map. Unlike
385:             * {@link ValidationComponentUtils#getAssociatedResult(JComponent, Map)}
386:             * this method returns the empty result if the component has no keys set.
387:             *
388:             * @param comp     the component may be marked with a validation message key
389:             * @param keyMap   maps validation message keys to ValidationResults
390:             * @return the ValidationResult associated with the given component
391:             *     as provided by the specified validation key map
392:             *     or <code>ValidationResult.EMPTY</code> if the component has no message key set,
393:             *     or <code>ValidationResult.EMPTY</code> if no result is associated
394:             *     with the component
395:             */
396:            private static ValidationResult getAssociatedResult(
397:                    JComponent comp, Map<Object, ValidationResult> keyMap) {
398:                ValidationResult result = ValidationComponentUtils
399:                        .getAssociatedResult(comp, keyMap);
400:                return result == null ? ValidationResult.EMPTY : result;
401:            }
402:
403:            // Event Handling *********************************************************
404:
405:            private void updateFeedbackComponents() {
406:                removeAllFeedbackComponents();
407:                visitComponentTree(content, model.getResult().keyMap(), 0, 0);
408:                repaint();
409:            }
410:
411:            /**
412:             * Ensures that the feedback components are repositioned.
413:             * Invoked by <code>#validate</code>, i. e. if this panel is laid out.<p>
414:             *
415:             * TODO: Improve this implementation to set only positions.
416:             * The current implementation removes all components and re-adds
417:             * them later.
418:             */
419:            private void repositionFeedbackComponents() {
420:                updateFeedbackComponents();
421:            }
422:
423:            /**
424:             * Recursively descends the container tree and recomputes the
425:             * layout for any subtrees marked as needing it (those marked as
426:             * invalid). In addition to the superclass behavior, we reposition
427:             * the feedback components after the child components have been
428:             * validated.<p>
429:             *
430:             * We reposition the feedback components only, if this panel is visible;
431:             * if it becomes visible, #validateTree will be invoked.
432:             *
433:             * @see Container#validateTree()
434:             * @see #validate()
435:             * @see #invalidate()
436:             * @see #doLayout()
437:             * @see Component#setVisible(boolean)
438:             * @see LayoutManager
439:             */
440:            @Override
441:            protected void validateTree() {
442:                super .validateTree();
443:                if (isVisible()) {
444:                    repositionFeedbackComponents();
445:                }
446:            }
447:
448:            /**
449:             * Gets notified when the ValidationResult changed and updates
450:             * the feedback components.
451:             */
452:            private final class ValidationResultChangeHandler implements 
453:                    PropertyChangeListener {
454:
455:                public void propertyChange(PropertyChangeEvent evt) {
456:                    updateFeedbackComponents();
457:                }
458:
459:            }
460:
461:            // Layout *****************************************************************
462:
463:            /**
464:             * Used to lay out the content layer in the icon feedback JLayeredPane.
465:             * The content fills the parent's space; minimum and preferred size of
466:             * this layout are requested from the content panel.
467:             */
468:            private final class SimpleLayout implements  LayoutManager {
469:
470:                /**
471:                 * If the layout manager uses a per-component string,
472:                 * adds the component <code>comp</code> to the layout,
473:                 * associating it
474:                 * with the string specified by <code>name</code>.
475:                 *
476:                 * @param name the string to be associated with the component
477:                 * @param comp the component to be added
478:                 */
479:                public void addLayoutComponent(String name, Component comp) {
480:                    // components are well known by the container
481:                }
482:
483:                /**
484:                 * Removes the specified component from the layout.
485:                 * @param comp the component to be removed
486:                 */
487:                public void removeLayoutComponent(Component comp) {
488:                    // components are well known by the container
489:                }
490:
491:                /**
492:                 * Calculates the preferred size dimensions for the specified
493:                 * container, given the components it contains.
494:                 *
495:                 * @param parent the container to be laid out
496:                 * @return the preferred size of the given container
497:                 * @see #minimumLayoutSize(Container)
498:                 */
499:                public Dimension preferredLayoutSize(Container parent) {
500:                    return content.getPreferredSize();
501:                }
502:
503:                /**
504:                 * Calculates the minimum size dimensions for the specified
505:                 * container, given the components it contains.
506:                 *
507:                 * @param parent the component to be laid out
508:                 * @return the minimum size of the given container
509:                 * @see #preferredLayoutSize(Container)
510:                 */
511:                public Dimension minimumLayoutSize(Container parent) {
512:                    return content.getMinimumSize();
513:                }
514:
515:                /**
516:                 * Lays out the specified container.
517:                 *
518:                 * @param parent the container to be laid out
519:                 */
520:                public void layoutContainer(Container parent) {
521:                    Dimension size = parent.getSize();
522:                    content.setBounds(0, 0, size.width, size.height);
523:                }
524:
525:            }
526:
527:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.