All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.alantea.swing.mindmap.MindMapViewer Maven / Gradle / Ivy

package net.alantea.swing.mindmap;

import java.awt.Canvas;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.util.LinkedList;
import java.util.List;

@SuppressWarnings("serial")
public class MindMapViewer extends Canvas
{
   private MindMapNode rootNode = new MindMapNode<>(null, null);
   
   private T selected = null;

   public MindMapViewer()
   {
      super();      
   }
   
   public MindMapNode getRootNode()
   {
      return rootNode;
   }
   
   public void setSelected(T target)
   {
      selected = target;
      repaint();
   }
   
   public T getSelected()
   {
      return selected;
   }
   
   public void setRootNode(MindMapNode node)
   {
      rootNode = node;
      repaint();
   }

   private static final int DELTA_X = 30;
   private static final int START_X = 5;
   private static final int GAP_Y = 8;
   
   private int centralX = Integer.MAX_VALUE;
   private int centralY = Integer.MAX_VALUE;
   
   public void setCenter(int x, int y)
   {
      centralX = x;
      centralY = y;
      repaint();
   }
   
   public void resetCenter()
   {
      centralX = Integer.MAX_VALUE;
      centralY = Integer.MAX_VALUE;
      repaint();
   }
   
   @Override
   public void paint(Graphics gc)
   {
      Rectangle rect = getBounds();
      
      int x = (centralX == Integer.MAX_VALUE) ? (rect.width / 2) : centralX;      
      int y = (centralY == Integer.MAX_VALUE) ? (rect.height / 2) : centralY;
      drawNodeCenter(getRootNode(), gc, x, y);
   }

   /***
    * x : length of text in pixels
    * y : base of text in pixels
    * width : width including subnodes
    * height : height including subnodes
    * 
    * @param node
    * @param gc
    * @return
    */
   private Rectangle calculateSpan(MindMapNode node, Graphics graphics)
   {
      Graphics2D gc = (Graphics2D) graphics;
      Point txtSize = stringExtent(gc, node.toString());
      txtSize.y += GAP_Y;
      Rectangle ret = new Rectangle(0, 0, txtSize.x + DELTA_X, 0);
      for (MindMapNode subnode : node.getChildren())
      {
         Rectangle subret = calculateSpan(subnode, gc);
         ret.width = Math.max(ret.width, subret.width + txtSize.x + DELTA_X);
         ret.height += subret.height;
      }
      
      if (ret.height < txtSize.y)
      {
         ret.height = txtSize.y;
      }
      ret.x = txtSize.x;
      ret.y = (ret.height + txtSize.y) / 2;
      return ret;
   }

   private void drawNodeCenter(MindMapNode node, Graphics graphics, int xcenter, int ycenter)
   {
      Graphics2D gc = (Graphics2D) graphics;
      List sizes = new LinkedList<>();
      int allHeights = 0;
      for (MindMapNode subnode : node.getChildren())
      {
         Rectangle subret = calculateSpan(subnode, gc);
         sizes.add(subret);
         allHeights += subret.height;
      }
      
      int n = -1;
      int midHeights = 0;
      int rightHeight = 0;
      int leftHeight = 0;
      int rightWidth = 0;
      int leftWidth = 0;
      for (int i = 0; i < sizes.size(); i++)
      {
         Rectangle size = sizes.get(i);
         midHeights += size.height;
 
         if (n == -1)
         {
            rightHeight += size.height;
            rightWidth = Math.max(rightWidth, size.width);
            if (midHeights >= allHeights / 2)
            {
               n = i;
            }
         }
         else
         {
            leftHeight += size.height;
            leftWidth = Math.max(leftWidth, size.width);
         }
      }

      Font oldFont = gc.getFont();
      Font newFont = new Font(oldFont.getFontName(), oldFont.getStyle(), oldFont.getSize() * 2);
      gc.setFont(newFont);
      Point txtSize = stringExtent(gc, node.toString());
      FontMetrics fm = gc.getFontMetrics();
      txtSize.y += GAP_Y;
      int y = ycenter - txtSize.y / 2;
      gc.drawString(node.toString(), xcenter - txtSize.x / 2, y + fm.getAscent() + GAP_Y / 2);
      gc.setFont(oldFont);
      gc.drawRoundRect(xcenter - txtSize.x / 2 - START_X, y, txtSize.x + 2 * START_X - 1, txtSize.y, START_X, START_X);

      List> subnodes = node.getChildren();
      int suby = ycenter - rightHeight / 2;
      for (int i = 0; i <= n; i++)
      {
         drawNodeRight(subnodes.get(i), gc, xcenter + txtSize.x / 2 + DELTA_X, suby - GAP_Y / 2);
         Point subtxtSize = stringExtent(gc, subnodes.get(i).toString());
         System.out.println(subtxtSize);
         subtxtSize.y +=  GAP_Y;
         gc.drawRoundRect(xcenter + txtSize.x / 2 + DELTA_X - START_X,
               suby + sizes.get(i).y - subtxtSize.y +  GAP_Y / 4,
               subtxtSize.x + 2 * START_X - 1,
               subtxtSize.y - GAP_Y / 2,
               START_X, START_X);
         int yf = suby + sizes.get(i).y - subtxtSize.y +  GAP_Y / 4 + (subtxtSize.y - GAP_Y / 2) / 2;
       gc.drawLine(xcenter + txtSize.x / 2 + START_X, ycenter,
             xcenter + txtSize.x / 2 + DELTA_X - START_X, yf);
       
         suby += sizes.get(i).height;
      }

      suby = ycenter + leftHeight / 2;
      for (int i = n + 1; i  node, Graphics graphics, int x, int y)
   {
      Graphics2D gc = (Graphics2D) graphics;
      Point txtSize = stringExtent(gc, node.toString());
      txtSize.y += GAP_Y;
      Rectangle span = calculateSpan(node, gc);
      int dry = y + (span.height - txtSize.y) / 2;
      FontMetrics fm = gc.getFontMetrics();
      gc.drawString(node.toString(), x, dry + GAP_Y + fm.getAscent());
      
      int subx = x + txtSize.x + DELTA_X;
      int suby = y;
      int maxdx = 0;
      for (MindMapNode subnode : node.getChildren())
      {
         Rectangle subret = calculateSpan(subnode, gc);
         drawNodeRight(subnode, gc, subx, suby);
         int middlex = (subx + x + txtSize.x) / 2;
         gc.drawLine(x + txtSize.x + START_X, dry + txtSize.y, middlex, dry + txtSize.y);
         gc.drawLine(middlex, dry + txtSize.y, middlex, suby + subret.y);
         gc.drawLine(middlex, suby + subret.y, subx + subret.x, suby + subret.y);
         suby += subret.y;
         if (maxdx < subret.x)
         {
            maxdx = subret.x;
         }
      }
      if (suby == 0)
      {
         suby = txtSize.y;
      }
      return new Rectangle(x, y, maxdx + txtSize.x, suby);
   }

   private Rectangle drawNodeLeft(MindMapNode node, Graphics graphics, int x, int y)
   {
      Graphics2D gc = (Graphics2D) graphics;
      Point txtSize = stringExtent(gc, node.toString());
      txtSize.y += GAP_Y;
      Rectangle span = calculateSpan(node, gc);
      int dry = y + (span.height - txtSize.y) / 2;
      FontMetrics fm = gc.getFontMetrics();
      gc.drawString(node.toString(), x + span.width - txtSize.x, dry + GAP_Y + fm.getAscent());
      
      int subx = x + span.width - span.x - DELTA_X;
      int suby = y;
      int maxdx = 0;
      for (MindMapNode subnode : node.getChildren())
      {
         Rectangle subret = calculateSpan(subnode, gc);
         drawNodeLeft(subnode, gc, subx - subret.width, suby);
         
         gc.drawLine(x + span.width - span.x - START_X, dry + txtSize.y,
                     x + span.width - span.x - DELTA_X / 2, dry + txtSize.y);
         gc.drawLine(x + span.width - span.x - DELTA_X / 2, dry + txtSize.y,
                     x + span.width - span.x - DELTA_X / 2, suby + subret.y);
         gc.drawLine(x + span.width - span.x - DELTA_X / 2, suby + subret.y,
                     x + span.width - span.x - DELTA_X - subret.x, suby + subret.y);
         suby += subret.y;
         if (maxdx < subret.x)
         {
            maxdx = subret.x;
         }
      }
      if (suby == 0)
      {
         suby = txtSize.y;
      }
      return new Rectangle(x, y, maxdx + txtSize.x, suby);
   }
   
   private Point stringExtent(Graphics gc, String text)
   {
      Font font = gc.getFont();
      AffineTransform affinetransform = new AffineTransform();     
      FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
      int textwidth = (int)(font.getStringBounds(text, frc).getWidth());
      int textheight = (int)(font.getStringBounds(text, frc).getHeight());
      return new Point(textwidth, textheight);
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy