Google Tag Manager

2024/06/30

Drag and drop to rearrange nodes in the JTree

Code

class TreeTransferHandler extends TransferHandler {
  private final DataFlavor nodesFlavor = new DataFlavor(
      List.class, "List of TreeNode");

  @Override public int getSourceActions(JComponent c) {
    return c instanceof JTree
        && TreeUtils.canStartDrag((JTree) c) ? COPY_OR_MOVE : NONE;
  }

  @Override protected Transferable createTransferable(JComponent c) {
    Transferable transferable = null;
    if (c instanceof JTree && ((JTree) c).getSelectionPaths() != null) {
      List<MutableTreeNode> copies = new ArrayList<>();
      Arrays.stream(((JTree) c).getSelectionPaths()).forEach(path -> {
        DefaultMutableTreeNode node =
            (DefaultMutableTreeNode) path.getLastPathComponent();
        DefaultMutableTreeNode clone =
            new DefaultMutableTreeNode(node.getUserObject());
        copies.add(TreeUtils.deepCopy(node, clone));
      });
      transferable = new Transferable() {
        @Override public DataFlavor[] getTransferDataFlavors() {
          return new DataFlavor[] {nodesFlavor};
        }

        @Override public boolean isDataFlavorSupported(
            DataFlavor flavor) {
          return Objects.equals(nodesFlavor, flavor);
        }

        @Override public Object getTransferData(DataFlavor flavor)
            throws UnsupportedFlavorException {
          if (isDataFlavorSupported(flavor)) {
            return copies;
          } else {
            throw new UnsupportedFlavorException(flavor);
          }
        }
      };
    }
    return transferable;
  }

  @Override public boolean canImport(TransferSupport support) {
    DropLocation dl = support.getDropLocation();
    Component c = support.getComponent();
    return support.isDrop()
        && support.isDataFlavorSupported(nodesFlavor)
        && c instanceof JTree
        && dl instanceof JTree.DropLocation
        && TreeUtils.canImportDropLocation(
            (JTree) c, (JTree.DropLocation) dl);
  }

  @Override public boolean importData(TransferSupport support) {
    Component c = support.getComponent();
    DropLocation dl = support.getDropLocation();
    Transferable transferable = support.getTransferable();
    return canImport(support)
        && c instanceof JTree
        && dl instanceof JTree.DropLocation
        && insertNode((JTree) c, (JTree.DropLocation) dl, transferable);
  }

  private boolean insertNode(
      JTree tree, JTree.DropLocation dl, Transferable transferable) {
    TreePath path = dl.getPath();
    Object p = path.getLastPathComponent();
    TreeModel m = tree.getModel();
    List<?> nodes = getTransferData(transferable);
    if (p instanceof MutableTreeNode && m instanceof DefaultTreeModel) {
      MutableTreeNode parent = (MutableTreeNode) p;
      DefaultTreeModel model = (DefaultTreeModel) m;
      int childIndex = dl.getChildIndex();
      AtomicInteger index = new AtomicInteger(
          getDropIndex(parent, childIndex));
      nodes.stream()
          .filter(MutableTreeNode.class::isInstance)
          .map(MutableTreeNode.class::cast)
          .forEach(n -> model.insertNodeInto(
               n, parent, index.getAndIncrement()));
    }
    return !nodes.isEmpty();
  }

  private static int getDropIndex(
      MutableTreeNode parent, int childIndex) {
    // Configure for drop mode.
    int index = childIndex; // DropMode.INSERT
    if (childIndex == -1) { // DropMode.ON
      index = parent.getChildCount();
    }
    return index;
  }

  private List<?> getTransferData(Transferable t) {
    List<?> nodes;
    try {
      nodes = (List<?>) t.getTransferData(nodesFlavor);
    } catch (UnsupportedFlavorException | IOException ex) {
      nodes = Collections.emptyList();
    }
    return nodes;
  }

  @Override protected void exportDone(
      JComponent src, Transferable data, int action) {
    if (src instanceof JTree && (action & MOVE) == MOVE) {
      cleanup((JTree) src);
    }
  }

  private void cleanup(JTree tree) {
    DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
    TreePath[] selectionPaths = tree.getSelectionPaths();
    if (selectionPaths != null) {
      for (TreePath path : selectionPaths) {
        model.removeNodeFromParent(
            (MutableTreeNode) path.getLastPathComponent());
      }
    }
  }
}

References

2024/05/31

Rounding the corners of a rectilinear polygon generated by node selection in a JTree

Code

public static List<Point2D> flatteningStepsOnRightSide(
    List<Point2D> list, double arc) {
  int sz = list.size();
  for (int i = 0; i < sz; i++) {
    int i1 = (i + 1) % sz;
    int i2 = (i + 2) % sz;
    int i3 = (i + 3) % sz;
    Point2D pt0 = list.get(i);
    Point2D pt1 = list.get(i1);
    Point2D pt2 = list.get(i2);
    Point2D pt3 = list.get(i3);
    double dx1 = pt2.getX() - pt1.getX();
    if (Math.abs(dx1) > 1.0e-1 && Math.abs(dx1) < arc) {
      double max = Math.max(pt0.getX(), pt2.getX());
      replace(list, i, max, pt0.getY());
      replace(list, i1, max, pt1.getY());
      replace(list, i2, max, pt2.getY());
      replace(list, i3, max, pt3.getY());
    }
  }
  return list;
}

private static void replace(List<Point2D> list, int i, double x, double y) {
  list.remove(i);
  list.add(i, new Point2D.Double(x, y));
}

/**
 * Rounding the corners of a Rectilinear Polygon.
 */
public static Path2D convertRoundedPath(List<Point2D> list, double arc) {
  double kappa = 4d * (Math.sqrt(2d) - 1d) / 3d; // = 0.55228...;
  double akv = arc - arc * kappa;
  int sz = list.size();
  Point2D pt0 = list.get(0);
  Path2D path = new Path2D.Double();
  path.moveTo(pt0.getX() + arc, pt0.getY());
  for (int i = 0; i < sz; i++) {
    Point2D prv = list.get((i - 1 + sz) % sz);
    Point2D cur = list.get(i);
    Point2D nxt = list.get((i + 1) % sz);
    double dx0 = signum(cur.getX() - prv.getX(), arc);
    double dy0 = signum(cur.getY() - prv.getY(), arc);
    double dx1 = signum(nxt.getX() - cur.getX(), arc);
    double dy1 = signum(nxt.getY() - cur.getY(), arc);
    path.curveTo(
        cur.getX() - dx0 * akv, cur.getY() - dy0 * akv,
        cur.getX() + dx1 * akv, cur.getY() + dy1 * akv,
        cur.getX() + dx1 * arc, cur.getY() + dy1 * arc);
    path.lineTo(nxt.getX() - dx1 * arc, nxt.getY() - dy1 * arc);
  }
  path.closePath();
  return path;
}

private static double signum(double v, double arc) {
  return Math.abs(v) < arc ? 0d : Math.signum(v);
}

References

2024/04/30

Rounding the corners of a rectilinear polygon generated by a selection of calendar made with JList

Code

public static Path2D convertRoundedPath(List<Point2D> list, double arc) {
  double kappa = 4d * (Math.sqrt(2d) - 1d) / 3d; // = 0.55228...;
  double akv = arc - arc * kappa;
  int sz = list.size();
  Point2D pt0 = list.get(0);
  Path2D path = new Path2D.Double();
  path.moveTo(pt0.getX() + arc, pt0.getY());
  for (int i = 0; i < sz; i++) {
    Point2D prv = list.get((i - 1 + sz) % sz);
    Point2D cur = list.get(i);
    Point2D nxt = list.get((i + 1) % sz);
    double dx0 = Math.signum(cur.getX() - prv.getX());
    double dy0 = Math.signum(cur.getY() - prv.getY());
    double dx1 = Math.signum(nxt.getX() - cur.getX());
    double dy1 = Math.signum(nxt.getY() - cur.getY());
    path.curveTo(
        cur.getX() - dx0 * akv, cur.getY() - dy0 * akv,
        cur.getX() + dx1 * akv, cur.getY() + dy1 * akv,
        cur.getX() + dx1 * arc, cur.getY() + dy1 * arc);
    path.lineTo(nxt.getX() - dx1 * arc, nxt.getY() - dy1 * arc);
  }
  path.closePath();
  return path;
}

References

2024/03/31

Switching between JToolBar and JMenuBar

Click on the hamburger menu-like JButton placed on the JToolBar to switch this with the JMenuBar

Code

JMenuBar mainMenuBar = makeMenuBar();
JButton button = makeHamburgerMenuButton(mainMenuBar);
JMenuBar wrappingMenuBar = new JMenuBar();
wrappingMenuBar.add(makeToolBar(button));
EventQueue.invokeLater(() -> getRootPane().setJMenuBar(wrappingMenuBar));

PopupMenuListener handler = new PopupMenuListener() {
  @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    // not need
  }

  @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    EventQueue.invokeLater(() -> {
      if (MenuSelectionManager.defaultManager().getSelectedPath().length == 0) {
        getRootPane().setJMenuBar(wrappingMenuBar);
      }
    });
  }

  @Override public void popupMenuCanceled(PopupMenuEvent e) {
    EventQueue.invokeLater(() -> getRootPane().setJMenuBar(wrappingMenuBar));
  }
};
for (int i = 0; i < mainMenuBar.getMenuCount(); i++) {
  mainMenuBar.getMenu(i).getPopupMenu().addPopupMenuListener(handler);
}
// ...
private JButton makeHamburgerMenuButton(JMenuBar menuBar) {
  JButton button = new JButton("Ξ") {
    @Override public Dimension getPreferredSize() {
      Dimension d = super.getPreferredSize();
      d.height = menuBar.getMenu(0).getPreferredSize().height;
      return d;
    }

    @Override public void updateUI() {
      super.updateUI();
      setContentAreaFilled(false);
      setFocusPainted(false);
      setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
    }
  };
  button.addActionListener(e -> {
    getRootPane().setJMenuBar(menuBar);
    getRootPane().revalidate();
    EventQueue.invokeLater(() -> menuBar.getMenu(0).doClick());
  });
  button.setMnemonic('\\');
  button.setToolTipText("Main Menu(Alt+\\)");
  return button;
}

References

2024/02/29

Paint JMenuItem selection rollover with rounded rectangle

Code

class BasicRoundMenuItemUI extends BasicMenuItemUI {
  @Override protected void paintBackground(
      Graphics g, JMenuItem menuItem, Color bgColor) {
    ButtonModel m = menuItem.getModel();
    Color oldColor = g.getColor();
    int menuWidth = menuItem.getWidth();
    int menuHeight = menuItem.getHeight();
    if (menuItem.isOpaque()) {
      if (m.isArmed() || (menuItem instanceof JMenu && m.isSelected())) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(
          RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // g2.clearRect(0, 0, menuWidth, menuHeight);
        g2.setPaint(menuItem.getBackground());
        g2.fillRect(0, 0, menuWidth, menuHeight);
        g2.setColor(bgColor);
        g2.fillRoundRect(2, 2, menuWidth - 4, menuHeight - 4, 8, 8);
        g2.dispose();
      } else {
        g.setColor(menuItem.getBackground());
        g.fillRect(0, 0, menuWidth, menuHeight);
      }
      g.setColor(oldColor);
    } else if (m.isArmed() || (menuItem instanceof JMenu && m.isSelected())) {
      g.setColor(bgColor);
      g.fillRoundRect(2, 2, menuWidth - 4, menuHeight - 4, 8, 8);
      g.setColor(oldColor);
    }
  }
}

References