/* * #! * Ontopia Vizigator * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ package net.ontopia.topicmaps.viz; import java.awt.Color; import java.awt.Graphics; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.utils.StringifierIF; import com.touchgraph.graphlayout.Node; import com.touchgraph.graphlayout.TGPanel; /** * INTERNAL: Node class representing topics. */ public class TMTopicNode extends TMAbstractNode { private int shapePadding; // We need to hold our own version of this for GUI display purposes in the // Vizlet private int associationCount = 0; protected boolean forceColor = false; private TopicIF topic; public static int DEFAULT_SHAPE_PADDING = 0; public static int MAX_SHAPE_PADDING = 20; private StringifierIF stringifier; private TopicIF scopingTopic; public TMTopicNode(TopicIF topic, TopicIF scopingTopic, TopicMapView topicMapView) { super(topic.getObjectId()); this.topicMapView = topicMapView; this.topic = topic; this.setScopingTopic(scopingTopic); updateLabel(); } public void updateLabel() { setLabel(getTopicName()); } public void setScopingTopic(TopicIF aScopingTopic) { scopingTopic = aScopingTopic; this.setStringifier(VizUtils.stringifierFor(scopingTopic)); } public String getTopicName() { return this.getStringifier().toString(topic); } /** * Repaints the name. Some topic names are shortened when initially displayed. * When the user moves the mouse over the node the full name is displayed. */ public void paint(Graphics g, TGPanel tgPanel) { setUnderMouse(tgPanel); String oldLabel = this.getLabel(); if (underMouse) { topicMapView.setHighlightNode(this, g); super.setLabel(this.getStringifier().toString(topic)); } else if (tgPanel.getMouseOverN() == null && tgPanel.getSelect() == this) { topicMapView.setHighlightNode(null, g); } miniPaint(g, tgPanel); if (!intersects(tgPanel.getSize())) return; this.paintNodeBody(g, tgPanel); this.drawMissingEdgesIndicator(g, tgPanel); if (icon != null) { icon.paintIcon(tgPanel, g, (int) drawx - (this.getWidth() / 2) - icon.getIconWidth(), (int) drawy - (icon.getIconHeight() / 2)); } super.setLabel(oldLabel); } public void miniPaint(Graphics g, TGPanel tgPanel) { String oldLabel = this.getLabel(); if (!intersects(tgPanel.getSize())) return; this.paintNodeBody(g, tgPanel); this.drawMissingEdgesIndicator(g, tgPanel); if (icon != null) { icon.paintIcon(tgPanel, g, (int) drawx - (this.getWidth() / 2) - icon.getIconWidth(), (int) drawy - (icon.getIconHeight() / 2)); } super.setLabel(oldLabel); } protected void drawMissingEdgesIndicator(Graphics g, TGPanel tgPanel) { int hiddenEdgeCount = associationCount - visibleEdgeCount(); if (hiddenEdgeCount <= 0) return; int ix = (int) drawx; int iy = (int) drawy; int h = getHeight(); int w = getWidth(); int tagX = ix + (w - 7) / 2 - 2 + w % 2; int tagY = iy - h / 2 - 2; char character; character = (hiddenEdgeCount < 9) ? (char) ('0' + hiddenEdgeCount) : '*'; paintSmallTag(g, tgPanel, tagX, tagY, Color.red, Color.white, character); } public void setLabel(String name) { int maxNameLength = topicMapView.getMaxTopicNameLength(); if (name != null && name.length() > maxNameLength) name = name.substring(0, maxNameLength) + "..."; super.setLabel(name); } public TopicIF getTopic() { return topic; } public void setShapePadding(int value) { shapePadding = value; } public int getShapePadding() { return this.shapePadding; } public int getWidth() { return super.getWidth() + this.getShapePadding(); } public int getHeight() { return super.getHeight() + this.getShapePadding(); } /** * This method enables TMTopicNode to force a particular color upon a given * node, even if it is the focus node. * This was necessary because blinking didn't work on the focus node. */ public Color getPaintBackColor(TGPanel tgPanel) { if (forceColor) return getBackColor(); else return super.getPaintBackColor(tgPanel); } public void repaint(Color aColor, TGPanel tgPanel) { repaint(aColor, tgPanel, false); } public void repaint(Color aColor, TGPanel tgPanel, boolean forceColor) { Color oldValue = getBackColor(); setBackColor(aColor); this.forceColor = forceColor; paint(tgPanel.getGraphics(), tgPanel); this.forceColor = false; setBackColor(oldValue); } public void setAssociationCount(int count) { this.associationCount = count; } public boolean containsPoint(double aPx, double aPy) { if (this.getType() == Node.TYPE_CIRCLE) { double deltaX = Math.abs((drawx - aPx) / 2); double deltaY = Math.abs((drawy - aPy) / 2); double result = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); int radius = (this.getWidth() / 4); return result <= radius; } else if (this.getType() == Node.TYPE_ELLIPSE) { double deltaX = Math.abs((drawx - aPx) / 2); double deltaY = Math.abs((drawy - aPy) / 2); double result = (Math.pow(deltaX, 2) / Math.pow(this.getWidth() / 4, 2)) + (Math.pow(deltaY, 2) / Math.pow(this.getHeight() / 4, 2)); return result <= 1; } return super.containsPoint(aPx, aPy); } public StringifierIF getStringifier() { return this.stringifier; } private void setStringifier(StringifierIF aStringifier) { this.stringifier = aStringifier; this.setLabel(stringifier.toString(topic)); } public RecoveryObjectIF getDesctructor() { return new DeleteTMTopicNode(getTopic()); } public RecoveryObjectIF getRecreator() { return new CreateTMTopicNode(getTopic()); } }