Revision: 3598
Author: [email protected]
Date: Fri Jun 11 09:19:49 2010
Log: NEW - bug 2458: Create Critic Manager
http://trillian.sqlpower.ca/bugzilla/show_bug.cgi?id=2458

Added in CriticBadges to the play pen. This allows objects that have criticisms on them to appear in the play pen with a badge beside them. The idea of a transient child has been added to the PlayPenConetentPane. At current it is a type of SPObject that has a correct parent pointer but changes do not fire events, which would lead to it being persisted, or gets returned in getChildren. We should see if there are other places where these kinds of child types may be of interest and formalize how transient children work. A method for getting a UI point for model objects has been added to the UI components. This gives each component that has an external badge the chance to say where the badge should go. For relationships this ends up in the middle of the line which can cover some of a relationship's label which could be improved.
http://code.google.com/p/power-architect/source/detail?r=3598

Added:
/trunk/src/main/java/ca/sqlpower/architect/swingui/critic/BasicCriticBadgeUI.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticBadge.java
/trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticSwingUtil.java
Modified:
 /trunk/src/main/java/ca/sqlpower/architect/swingui/ArchitectFrame.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/BasicRelationshipUI.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/BasicTablePaneUI.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPen.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenComponent.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenComponentUI.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenContentPane.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticPanel.java
 /trunk/src/main/java/ca/sqlpower/architect/swingui/olap/OLAPPaneUI.java
/trunk/src/main/java/ca/sqlpower/architect/swingui/olap/UsageComponentUI.java
Replaced:
 /trunk/src/main/java/ca/sqlpower/architect/ddl/critic/CriticismBucket.java

=======================================
--- /dev/null
+++ /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/BasicCriticBadgeUI.java Fri Jun 11 09:19:49 2010
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010, SQL Power Group Inc.
+ *
+ * This file is part of SQL Power Architect.
+ *
+ * SQL Power Architect is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SQL Power Architect is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package ca.sqlpower.architect.swingui.critic;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Point;
+
+import javax.swing.ImageIcon;
+
+import ca.sqlpower.architect.ddl.critic.Criticism;
+import ca.sqlpower.architect.ddl.critic.CriticAndSettings.Severity;
+import ca.sqlpower.architect.swingui.PlayPenComponent;
+import ca.sqlpower.architect.swingui.PlayPenComponentUI;
+
+/**
+ * Simple critic badge UI.
+ */
+public class BasicCriticBadgeUI implements PlayPenComponentUI {
+
+    private final CriticBadge model;
+
+    public BasicCriticBadgeUI(CriticBadge model) {
+        this.model = model;
+    }
+
+    public boolean contains(Point p) {
+        Point location = model.getLocation();
+ return (location.x < p.x && p.x < location.x + getPreferredSize().width && + location.y < p.y && p.y < location.y + getPreferredSize().height);
+    }
+
+    public Dimension getPreferredSize() {
+ //All icons used by this UI must be of the same size or this will fail.
+        return new Dimension(CriticSwingUtil.ERROR_ICON.getIconWidth(),
+                CriticSwingUtil.ERROR_ICON.getIconHeight());
+    }
+
+    public void installUI(PlayPenComponent c) {
+ // TODO add listener to badge that is notified when there are changes
+        //to the criticisms being displayed.
+
+    }
+
+    public void paint(Graphics2D g2) {
+        ImageIcon icon = CriticSwingUtil.WARNING_ICON;
+        for (Criticism criticism : model.getCriticisms()) {
+ if (criticism.getCritic().getSeverity().equals(Severity.ERROR)) {
+                icon = CriticSwingUtil.ERROR_ICON;
+                break;
+            }
+        }
+        g2.drawImage(icon.getImage(), 0, 0, null);
+    }
+
+    public void revalidate() {
+ Point badgePoint = model.getUIOfSubject().getPointForModelObject(model.getSubject());
+        int xLoc = badgePoint.x - getPreferredSize().width;
+        int yLoc = badgePoint.y;
+ if (xLoc != model.getLocation().x || yLoc != model.getLocation().y) {
+            model.setLocation(new Point(xLoc, yLoc));
+        }
+    }
+
+    public void uninstallUI(PlayPenComponent c) {
+        // TODO remove listeners added in installUI
+    }
+
+    public Point getPointForModelObject(Object modelObject) {
+        return model.getLocation();
+    }
+}
=======================================
--- /dev/null
+++ /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticBadge.java Fri Jun 11 09:19:49 2010
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2010, SQL Power Group Inc.
+ *
+ * This file is part of SQL Power Architect.
+ *
+ * SQL Power Architect is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SQL Power Architect is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package ca.sqlpower.architect.swingui.critic;
+
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import ca.sqlpower.architect.ddl.critic.Criticism;
+import ca.sqlpower.architect.swingui.PlayPenComponent;
+import ca.sqlpower.object.AbstractPoolingSPListener;
+import ca.sqlpower.object.CleanupExceptions;
+import ca.sqlpower.object.SPChildEvent;
+import ca.sqlpower.object.SPListener;
+import ca.sqlpower.object.SPObject;
+
+/**
+ * A badge that appears in the play pen beside the object that is being
+ * criticized.
+ */
+public class CriticBadge extends PlayPenComponent {
+
+    /**
+     * The criticisms that this badge represents.
+     */
+    private final List<Criticism> criticisms;
+
+    /**
+ * The subject of the criticisms of this badge. All criticisms of this badge
+     * must be about this object.
+     */
+    private final Object subject;
+
+    /**
+ * A play pen component that will tell us where to display the critic badge.
+     */
+    private final PlayPenComponent UIOfSubject;
+
+    /**
+ * Listener attached to the {...@link #UIOfSubject} object to be notified when
+     * it moves. This lets us correct our location to give chase.
+     */
+ private final SPListener UISubjectMoveListener = new AbstractPoolingSPListener() {
+
+        @Override
+        public void propertyChangeImpl(PropertyChangeEvent evt) {
+            revalidate();
+        }
+    };
+
+    /**
+     * Listens for changes to the UISubject. If the subject is removed this
+     * badge will be removed as well.
+     */
+ private final SPListener UISubjectRemovedListener = new AbstractPoolingSPListener() {
+
+        @Override
+        public void childRemovedImpl(SPChildEvent e) {
+            if (e.getChild().equals(UIOfSubject)) {
+                getParent().removeCriticBadge(CriticBadge.this);
+                cleanup();
+            }
+        }
+
+        @Override
+        protected void childAddedImpl(SPChildEvent e) {
+            if (e.getChild().equals(UIOfSubject)) {
+                getParent().addCriticBadge(CriticBadge.this);
+                connect();
+            }
+        }
+    };
+
+    /**
+ * Listens for a removal of the subject object. If the model is removed this + * badge will be removed as well. This can only be done for SPObjects as we
+     * know how to check if an SPObject is being removed from its parent.
+     */
+ private final SPListener SubjectRemovedListener = new AbstractPoolingSPListener() {
+
+        public void childRemovedImpl(SPChildEvent e) {
+            if (e.getChild().equals(subject)) {
+                getParent().removeCriticBadge(CriticBadge.this);
+                cleanup();
+            }
+        }
+
+        @Override
+        protected void childAddedImpl(SPChildEvent e) {
+            if (e.getChild().equals(subject)) {
+                getParent().addCriticBadge(CriticBadge.this);
+                connect();
+            }
+        }
+    };
+
+ public CriticBadge(List<Criticism> criticisms, Object subject, PlayPenComponent UIOfSubject) {
+        super("Criticism of " + subject.toString());
+        this.criticisms = new ArrayList<Criticism>(criticisms);
+        this.subject = subject;
+        this.UIOfSubject = UIOfSubject;
+        BasicCriticBadgeUI ui = new BasicCriticBadgeUI(this);
+        ui.installUI(this);
+        setUI(ui);
+        updateToolTipText();
+        connect();
+    }
+
+    @Override
+    public void connect() {
+        UIOfSubject.addSPListener(UISubjectMoveListener);
+        if (UIOfSubject.getParent() != null) {
+ UIOfSubject.getParent().addSPListener(UISubjectRemovedListener);
+        }
+ if (subject instanceof SPObject && ((SPObject) subject).getParent() != null) { + ((SPObject) subject).getParent().addSPListener(SubjectRemovedListener);
+        }
+    }
+
+    public CleanupExceptions cleanup() {
+        UIOfSubject.removeSPListener(UISubjectMoveListener);
+        if (UIOfSubject.getParent() != null) {
+ UIOfSubject.getParent().removeSPListener(UISubjectRemovedListener);
+        }
+ if (subject instanceof SPObject && ((SPObject) subject).getParent() != null) { + ((SPObject) subject).getParent().removeSPListener(SubjectRemovedListener);
+        }
+        return new CleanupExceptions();
+    }
+
+    @Override
+    public List<Criticism> getModel() {
+        return criticisms;
+    }
+
+    @Override
+    public String getModelName() {
+        return getSubject().toString();
+    }
+
+    private void updateToolTipText() {
+        StringBuffer buffer = new StringBuffer();
+        for (Criticism criticism : criticisms) {
+            buffer.append(criticism.getDescription() + "\n");
+        }
+        setToolTipText(buffer.toString());
+    }
+
+    @Override
+    public void handleMouseEvent(MouseEvent evt) {
+ // TODO left click should highlight criticisms in the panel in the play pen + // TODO right click should give a menu option to ignore a critic on the subject.
+    }
+
+    /**
+ * Adds a criticism to this badge. The criticism must be on the subject of
+     * this badge.
+     */
+    public void addCriticism(Criticism c) {
+ if (!c.getSubject().equals(getSubject())) throw new IllegalArgumentException( + "Subject of new critic " + c.getSubject() + " is not " + getSubject());
+        criticisms.add(c);
+        updateToolTipText();
+    }
+
+    public void removeCriticism(Criticism c) {
+        criticisms.remove(c);
+        if (criticisms.isEmpty()) {
+            getParent().removeCriticBadge(CriticBadge.this);
+            cleanup();
+        }
+    }
+
+    public List<Criticism> getCriticisms() {
+        return Collections.unmodifiableList(criticisms);
+    }
+
+    public PlayPenComponent getUIOfSubject() {
+        return UIOfSubject;
+    }
+
+    public Object getSubject() {
+        return subject;
+    }
+
+    @Override
+    public void setParent(SPObject parent) {
+        super.setParent(parent);
+        revalidate();
+    }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticSwingUtil.java Fri Jun 11 09:19:49 2010
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, SQL Power Group Inc.
+ *
+ * This file is part of SQL Power Architect.
+ *
+ * SQL Power Architect is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SQL Power Architect is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package ca.sqlpower.architect.swingui.critic;
+
+import javax.swing.ImageIcon;
+
+import ca.sqlpower.swingui.SPSUtils;
+
+public class CriticSwingUtil {
+
+    /**
+ * Error icon to go along with criticisms that are flagged to be errors.
+     */
+ public static final ImageIcon ERROR_ICON = SPSUtils.createIcon("error", "error badge");
+
+    /**
+ * Warning icon to go along with criticisms that are flagged to be warnings.
+     */
+ public static final ImageIcon WARNING_ICON = SPSUtils.createIcon("warning", "warning badge");
+
+    private CriticSwingUtil() {
+        //utility class
+    }
+}
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/ArchitectFrame.java Wed Jun 9 11:52:17 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/ArchitectFrame.java Fri Jun 11 09:19:49 2010
@@ -1292,7 +1292,7 @@
      * Will also display the critic panel if it is not visible.
      */
     public void updateCriticPanel(List<Criticism> criticisms) {
- criticPanel.getCriticismBucket().updateCriticismsToMatch(criticisms);
+        playpen.getCriticismBucket().updateCriticismsToMatch(criticisms);
         criticPanel.getPanel().setVisible(true);
         final double screenHeight = splitPane.getHeight();
double viewHeight = Math.min(criticPanel.getPanel().getPreferredSize().getHeight(),
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/BasicRelationshipUI.java Fri May 7 08:33:40 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/BasicRelationshipUI.java Fri Jun 11 09:19:49 2010
@@ -1543,4 +1543,10 @@
            }
            return x;
        }
-}
+
+       public Point getPointForModelObject(Object modelObject) {
+           Dimension preferredSize = getPreferredSize();
+ return new Point(relationship.getLocation().x + (preferredSize.width / 2),
+                   relationship.getLocation().y + (preferredSize.height / 2));
+       }
+}
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/BasicTablePaneUI.java Mon May 3 06:53:28 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/BasicTablePaneUI.java Fri Jun 11 09:19:49 2010
@@ -521,4 +521,36 @@
             return fullTag.toString();
         }
     }
-}
+
+    public Point getPointForModelObject(Object modelObject) {
+        Point location = tablePane.getLocation();
+ if (!tablePane.getItems().contains(modelObject) || tablePane.getFont() == null) {
+            return location;
+        }
+
+        //This should line up with painting the columns
+        int y = location.y;
+        Font font = tablePane.getFont();
+        FontMetrics metrics = tablePane.getFontMetrics(font);
+        int fontHeight = metrics.getHeight();
+        //Title
+        y += fontHeight;
+
+        if (!((SQLColumn) modelObject).isPrimaryKey()) {
+            y += PK_GAP;
+        }
+        Iterator<SQLColumn> colNameIt = tablePane.getItems().iterator();
+        while (colNameIt.hasNext()) {
+            SQLColumn col = colNameIt.next();
+
+            if (col.equals(modelObject)) {
+                return new Point(location.x, y);
+            }
+
+            y += fontHeight;
+        }
+
+        //This should never really be reached.
+        return tablePane.getLocation();
+    }
+}
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPen.java Wed Jun 2 15:52:53 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPen.java Fri Jun 11 09:19:49 2010
@@ -92,6 +92,7 @@
 import org.apache.log4j.Logger;

 import ca.sqlpower.architect.ArchitectUtils;
+import ca.sqlpower.architect.ddl.critic.CriticismBucket;
 import ca.sqlpower.architect.olap.MondrianModel;
 import ca.sqlpower.architect.olap.OLAPObject;
 import ca.sqlpower.architect.olap.MondrianModel.Cube;
@@ -404,7 +405,7 @@
List<PlayPenComponent> relationshipsLast = new ArrayList<PlayPenComponent>(); List<Relationship> relations = contentPane.getChildren(Relationship.class); List<UsageComponent> usages = contentPane.getChildren(UsageComponent.class);
-           relationshipsLast.addAll(contentPane.getChildren());
+           relationshipsLast.addAll(contentPane.getAllChildren());
            relationshipsLast.removeAll(relations);
            relationshipsLast.addAll(relations);
            relationshipsLast.removeAll(usages);
@@ -604,6 +605,13 @@
         */
        private boolean ignoreTreeSelection = false;

+    /**
+ * The {...@link CriticismBucket} that stays around for the life of the panel. + * The criticisms in the panel can be updated in this bucket to be valid
+     * criticisms of the current project.
+     */
+    private final CriticismBucket criticismBucket = new CriticismBucket();
+
        public PlayPen(ArchitectSwingSession session) {
            this(session, session.getTargetDatabase());
        }
@@ -3438,5 +3446,9 @@
             lifecycleListeners.get(i).PlayPenLifeEnding(evt);
         }
     }
+
+    public CriticismBucket getCriticismBucket() {
+        return criticismBucket;
+    }

 }
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenComponent.java Wed May 26 14:48:19 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenComponent.java Fri Jun 11 09:19:49 2010
@@ -34,12 +34,14 @@
 import java.util.LinkedList;
 import java.util.List;

+import javax.annotation.Nonnull;
 import javax.swing.JOptionPane;
 import javax.swing.JPopupMenu;

 import org.apache.log4j.Logger;

 import ca.sqlpower.architect.ArchitectUtils;
+import ca.sqlpower.architect.ddl.critic.Criticism;
 import ca.sqlpower.architect.enterprise.NetworkConflictResolver;
import ca.sqlpower.architect.enterprise.NetworkConflictResolver.UpdateListener;
 import ca.sqlpower.architect.swingui.event.SelectionEvent;
@@ -726,5 +728,33 @@
     public void connect() {
         //by default do nothing.
     }
+
+    /**
+ * Collects a list of criticisms on the model object that this component is
+     * displaying.
+     */
+    public List<Criticism> findCriticisms() {
+ return getPlayPen().getCriticismBucket().getCriticismsByObject(getModel());
+    }
+
+    /**
+     * Returns a point on the UI object that is most reasonable to attach
+ * additional text or objects to for the given model object or a part of the + * model. If the component is made up of multiple parts the object passed in
+     * may change the desired location to put an icon near the part.
+     * <p>
+ * Default is the top left point of the component returned by getLocation().
+     *
+     * @param modelObject
+ * The model of this component or an object that is part of the
+     *            model, normally a descendant.
+ * @return A point that is the best location to place an icon or text at. If
+     *         the given modelObject does not belong to this component a
+     *         reasonable point for the component will still be returned.
+     */
+    @Transient @Accessor
+    public Point getPointForModelObject(@Nonnull Object modelObject) {
+        return getUI().getPointForModelObject(modelObject);
+    }

 }
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenComponentUI.java Tue Mar 25 07:38:28 2008 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenComponentUI.java Fri Jun 11 09:19:49 2010
@@ -22,6 +22,11 @@
 import java.awt.Graphics2D;
 import java.awt.Point;

+import javax.annotation.Nonnull;
+
+import ca.sqlpower.object.annotation.Accessor;
+import ca.sqlpower.object.annotation.Transient;
+
 /**
* The PlayPenComponentUI is the superclass of all UI delegates for Play Pen components. * It provides a pluggable look-and-feel for PlayPenComponents exactly the same way the
@@ -38,4 +43,22 @@

        public void revalidate();
        public Dimension getPreferredSize();
-}
+
+       /**
+     * Returns a point on the UI object that is most reasonable to attach
+ * additional text or objects to for the given model object or a part of the + * model. If the component is made up of multiple parts the object passed in
+     * may change the desired location to put an icon near the part.
+     * <p>
+ * Default is normally the top left point of the component returned by getLocation().
+     *
+     * @param modelObject
+ * The model of this component or an object that is part of the
+     *            model, normally a descendant.
+ * @return A point that is the best location to place an icon or text at. If
+     *         the given modelObject does not belong to this component a
+     *         reasonable point for the component will still be returned.
+     */
+    @Transient @Accessor
+    public Point getPointForModelObject(@Nonnull Object modelObject);
+}
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenContentPane.java Mon May 31 15:36:25 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/PlayPenContentPane.java Fri Jun 11 09:19:49 2010
@@ -27,11 +27,14 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;

 import org.apache.log4j.Logger;

-import ca.sqlpower.architect.ArchitectProject;
+import ca.sqlpower.architect.ddl.critic.CriticismEvent;
+import ca.sqlpower.architect.ddl.critic.CriticismListener;
 import ca.sqlpower.architect.olap.OLAPSession;
+import ca.sqlpower.architect.swingui.critic.CriticBadge;
 import ca.sqlpower.architect.swingui.olap.UsageComponent;
 import ca.sqlpower.object.AbstractSPListener;
 import ca.sqlpower.object.AbstractSPObject;
@@ -44,8 +47,14 @@
 import ca.sqlpower.object.annotation.ConstructorParameter;
 import ca.sqlpower.object.annotation.Mutator;
 import ca.sqlpower.object.annotation.NonBound;
+import ca.sqlpower.object.annotation.NonProperty;
 import ca.sqlpower.object.annotation.Transient;
+import ca.sqlpower.sqlobject.SQLColumn;
 import ca.sqlpower.sqlobject.SQLDatabase;
+import ca.sqlpower.sqlobject.SQLIndex;
+import ca.sqlpower.sqlobject.SQLRelationship;
+import ca.sqlpower.sqlobject.SQLTable;
+import ca.sqlpower.sqlobject.SQLRelationship.SQLImportedKey;

 public class PlayPenContentPane extends AbstractSPObject {
private static final Logger logger = Logger.getLogger(PlayPenContentPane.class);
@@ -109,6 +118,59 @@
         }

     };
+
+    /**
+ * Each badge in this list marks a UI object to have criticisms on the UI or + * model object that is the subject of the badge. These badges are transient + * and so stored here instead of in the content pane where they would then
+     * be persisted.
+     * <p>
+     * IMPORTANT NOTE! These badges are NOT part of the objects returned by
+ * getChildren. The badges are generated based on critics and neither the + * badges or the criticisms are to be persisted, instead they are generated. + * These badges may look like children but they are closer to being a kind
+     * of transient child.
+     */
+ private final Map<Object, CriticBadge> badges = new HashMap<Object, CriticBadge>();
+
+ private final CriticismListener criticismListener = new CriticismListener() {
+
+        public void criticismRemoved(CriticismEvent e) {
+            CriticBadge badge = badges.get(e.getCriticism().getSubject());
+            if (badge != null) {
+                badge.removeCriticism(e.getCriticism());
+                if (badge.getCriticisms().isEmpty()) {
+                    removeCriticBadge(badge);
+                }
+            }
+        }
+
+        public void criticismAdded(CriticismEvent e) {
+            Object subject = e.getCriticism().getSubject();
+            CriticBadge badge = badges.get(subject);
+            if (badge != null) {
+                badge.addCriticism(e.getCriticism());
+ } else if (subject instanceof SQLTable || subject instanceof SQLRelationship + || subject instanceof SQLIndex || subject instanceof SQLColumn ||
+                    subject instanceof SQLImportedKey) {
+ //XXX May want a more generic way to find targets that can be badged in the future.
+                Object UISubject;
+ if (subject instanceof SQLIndex || subject instanceof SQLColumn) {
+                    UISubject = ((SPObject) subject).getParent();
+                } else if (subject instanceof SQLImportedKey) {
+ UISubject = ((SQLImportedKey) subject).getRelationship();
+                } else {
+                    UISubject = subject;
+                }
+ PlayPenComponent ppc = getPlayPen().findPPComponent(UISubject);
+                if (ppc != null) {
+ badge = new CriticBadge(Collections.singletonList(e.getCriticism()),
+                            subject, ppc);
+                    addCriticBadge(badge);
+                }
+            }
+        }
+    };

        @Constructor
public PlayPenContentPane(@ConstructorParameter(propertyName="modelContainer") SPObject modelContainer) {
@@ -158,6 +220,7 @@
         this.playPen = owner;
         setPlayPenListeningToComponents();
         if (owner != null) {
+ owner.getCriticismBucket().addCriticismListener(criticismListener); owner.addPropertyChangeListener("zoom", new ZoomFixer()); //$NON-NLS-1$
             firePropertyChange("playPen", null, owner);
         }
@@ -182,7 +245,8 @@

     /**
      * Looks for tooltip text in the component under the pointer,
-     * respecting the current zoom level.
+     * respecting the current zoom level. Returns null if there is
+     * no object at the mouse event for the tool tip.
      */
     @Transient @Accessor
     public String getToolTipText(MouseEvent e) {
@@ -201,7 +265,7 @@
      */
     @NonBound
     public PlayPenComponent getComponentAt(Point p) {
-        for (PlayPenComponent ppc : getChildren(PlayPenComponent.class)) {
+        for (PlayPenComponent ppc : getAllChildren()) {
             if (ppc.contains(p)) {
                 return ppc;
             }
@@ -306,15 +370,28 @@
         children.addAll(dependentComponents);
         return children;
     }
+
+    /**
+     * Returns all of the 'children' of this content pane including the
+     * transient {...@link CriticBadge}s.
+     */
+    @NonProperty
+    public List<? extends PlayPenComponent> getAllChildren() {
+ List<PlayPenComponent> children = new ArrayList<PlayPenComponent>();
+        children.addAll(components);
+        children.addAll(badges.values());
+        children.addAll(dependentComponents);
+        return children;
+    }

     @Accessor
-    public ArchitectProject getParent() {
-        return (ArchitectProject) super.getParent();
+    public ArchitectSwingProject getParent() {
+        return (ArchitectSwingProject) super.getParent();
     }

     @Mutator
     public void setParent(SPObject parent) {
-        if (parent instanceof ArchitectProject || parent == null) {
+        if (parent instanceof ArchitectSwingProject || parent == null) {
             super.setParent(parent);
         } else {
throw new IllegalArgumentException("Parent of PlayPenContentPane must be " +
@@ -413,5 +490,25 @@
             ppc.addSelectionListener(getPlayPen());
         }
     }
+
+    /**
+ * Removes a critic badge from the content pane. The badges are a child type + * that is transient so this method does not fire child events that would
+     * cause persist calls.
+     */
+    public void removeCriticBadge(CriticBadge badge) {
+        badges.remove(badge.getSubject());
+        badge.cleanup();
+    }
+
+    /**
+     * Adds a critic badge to the content pane. The badges are a child type
+ * that is transient so this method does not fire child events that would
+     * cause persist calls.
+     */
+    public void addCriticBadge(CriticBadge badge) {
+        badge.setParent(PlayPenContentPane.this);
+        badges.put(badge.getSubject(), badge);
+    }

 }
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticPanel.java Wed Jun 9 11:52:17 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/critic/CriticPanel.java Fri Jun 11 09:19:49 2010
@@ -29,10 +29,8 @@
 import javax.swing.table.DefaultTableCellRenderer;
 import javax.swing.table.TableCellRenderer;

-import ca.sqlpower.architect.ddl.critic.CriticismBucket;
 import ca.sqlpower.architect.ddl.critic.CriticAndSettings.Severity;
 import ca.sqlpower.architect.swingui.ArchitectSwingSession;
-import ca.sqlpower.swingui.SPSUtils;
 import ca.sqlpower.swingui.table.FancyExportableJTable;

 import com.jgoodies.forms.builder.ButtonBarBuilder;
@@ -43,16 +41,6 @@
  */
 public class CriticPanel {

-    /**
- * Error icon to go along with criticisms that are flagged to be errors.
-     */
- private static final ImageIcon ERROR_ICON = SPSUtils.createIcon("error", "error badge");
-
-    /**
- * Warning icon to go along with criticisms that are flagged to be warnings.
-     */
- private static final ImageIcon WARNING_ICON = SPSUtils.createIcon("warning", "warning badge");
-
     /**
      * A cell renderer that can display badges with the criticisms.
      */
@@ -69,9 +57,9 @@

         private ImageIcon getIcon(Severity severity) {
             if (severity == Severity.ERROR) {
-                return ERROR_ICON;
+                return CriticSwingUtil.ERROR_ICON;
             } else if (severity == Severity.WARNING) {
-                return WARNING_ICON;
+                return CriticSwingUtil.WARNING_ICON;
             } else {
                 return null;
             }
@@ -83,20 +71,12 @@
      */
     private JPanel panel;

-    /**
- * The {...@link CriticismBucket} that stays around for the life of the panel. - * The criticisms in the panel can be updated in this bucket to be valid
-     * criticisms of the current project.
-     */
-    private final CriticismBucket criticismBucket;
-
     private final ArchitectSwingSession session;

     public CriticPanel(ArchitectSwingSession session) {
         this.session = session;
-        criticismBucket = new CriticismBucket();
-
- CriticismTableModel tableModel = new CriticismTableModel(criticismBucket);
+
+ CriticismTableModel tableModel = new CriticismTableModel(session.getPlayPen().getCriticismBucket()); FancyExportableJTable table = new FancyExportableJTable(tableModel);
         table.setDefaultRenderer(Severity.class, tableRenderer);
         panel = new JPanel(new BorderLayout());
@@ -113,9 +93,5 @@
     public JPanel getPanel() {
         return panel;
     }
-
-    public CriticismBucket getCriticismBucket() {
-        return criticismBucket;
-    }

 }
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/olap/OLAPPaneUI.java Fri May 7 08:33:40 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/olap/OLAPPaneUI.java Fri Jun 11 09:19:49 2010
@@ -654,4 +654,8 @@
         }

     }
-}
+
+    public Point getPointForModelObject(Object modelObject) {
+        return olapPane.getLocation();
+    }
+}
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/swingui/olap/UsageComponentUI.java Wed Mar 17 14:29:59 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/swingui/olap/UsageComponentUI.java Fri Jun 11 09:19:49 2010
@@ -262,4 +262,7 @@
         uc.getPane2().addSPListener(componentEventHandler);
     }

-}
+    public Point getPointForModelObject(Object modelObject) {
+        return c.getLocation();
+    }
+}
=======================================
--- /trunk/src/main/java/ca/sqlpower/architect/ddl/critic/CriticismBucket.java Wed Jun 9 11:52:17 2010 +++ /trunk/src/main/java/ca/sqlpower/architect/ddl/critic/CriticismBucket.java Fri Jun 11 09:19:49 2010
@@ -20,8 +20,12 @@
 package ca.sqlpower.architect.ddl.critic;

 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+

 /**
  * This bucket holds all of the current criticisms about the state of the
@@ -71,6 +75,24 @@
     public List<Criticism> getCriticisms() {
         return Collections.unmodifiableList(criticisms);
     }
+
+    public List<Criticism> getCriticismsByObject(Object subject) {
+        List<Criticism> selectedCriticisms = new ArrayList<Criticism>();
+        for (Criticism criticism : criticisms) {
+            if (criticism.getSubject().equals(subject)) {
+                selectedCriticisms.add(criticism);
+            }
+        }
+        return selectedCriticisms;
+    }
+
+    public Collection<Object> getCriticismSubjects() {
+        Set<Object> subjects = new HashSet<Object>();
+        for (Criticism criticism : criticisms) {
+            subjects.add(criticism.getSubject());
+        }
+        return subjects;
+    }

     public void addCriticismListener(CriticismListener l) {
         listeners.add(l);

Reply via email to