Google Tag Manager

Showing posts with label JTabbedPane. Show all posts
Showing posts with label JTabbedPane. Show all posts

2018/12/27

aterai

Fade out JTabbedPane tab title on overflow instead of ellipsis

Code

class TextOverflowFadeTabbedPane extends ClippedTitleTabbedPane {
  protected TextOverflowFadeTabbedPane() {
    super();
  }

  protected TextOverflowFadeTabbedPane(int tabPlacement) {
    super(tabPlacement);
  }

  @Override public void insertTab(
        String title, Icon icon, Component component, String tip, int index) {
    super.insertTab(title, icon, component, Objects.toString(tip, title), index);
    JPanel p = new JPanel(new BorderLayout(2, 0));
    p.setOpaque(false);
    p.add(new JLabel(icon), BorderLayout.WEST);
    p.add(new TextOverflowFadeLabel(title));
    setTabComponentAt(index, p);
  }
}

class TextOverflowFadeLabel extends JLabel {
  private static final int LENGTH = 20;
  private static final float DIFF = .05f;

  protected TextOverflowFadeLabel(String text) {
    super(text);
  }

  @Override public void paintComponent(Graphics g) {
    Insets i = getInsets();
    int w = getWidth() - i.left - i.right;
    int h = getHeight() - i.top - i.bottom;
    Rectangle rect = new Rectangle(i.left, i.top, w - LENGTH, h);

    Graphics2D g2 = (Graphics2D) g.create();
    g2.setFont(g.getFont());
    g2.setPaint(getForeground());

    FontRenderContext frc = g2.getFontRenderContext();
    TextLayout tl = new TextLayout(getText(), getFont(), frc);
    int baseline = getBaseline(w, h);

    g2.setClip(rect);
    tl.draw(g2, getInsets().left, baseline);

    rect.width = 1;
    float alpha = 1f;
    for (int x = w - LENGTH; x < w; x++) {
      rect.x = x;
      alpha = Math.max(0f, alpha - DIFF);
      g2.setComposite(AlphaComposite.SrcOver.derive(alpha));
      g2.setClip(rect);
      tl.draw(g2, getInsets().left, baseline);
    }
    g2.dispose();
  }
}

References

2018/08/29

aterai

Change the tab of JTabbedPane to a flat design style

Code

UIManager.put("TabbedPane.tabInsets", new Insets(5, 10, 5, 10));
UIManager.put("TabbedPane.tabAreaInsets", new Insets(0, 0, 0, 0));
UIManager.put("TabbedPane.selectedLabelShift", 0);
UIManager.put("TabbedPane.labelShift", 0);

// UIManager.put("TabbedPane.foreground", Color.WHITE);
// UIManager.put("TabbedPane.selectedForeground", Color.WHITE);
// UIManager.put("TabbedPane.unselectedBackground", UNSELECTED_BG);
UIManager.put("TabbedPane.tabAreaBackground", UNSELECTED_BG);

JTabbedPane tabs = new JTabbedPane() {
  @Override public void updateUI() {
    super.updateUI();
    setUI(new BasicTabbedPaneUI() {
      @Override protected void paintFocusIndicator(
          Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex,
          Rectangle iconRect, Rectangle textRect, boolean isSelected) {
        // Do not paint anything
      }
      @Override protected void paintTabBorder(
          Graphics g, int tabPlacement, int tabIndex,
          int x, int y, int w, int h, boolean isSelected) {
        // Do not paint anything
      }
      @Override  protected void paintTabBackground(
          Graphics g, int tabPlacement, int tabIndex,
          int x, int y, int w, int h, boolean isSelected) {
        g.setColor(isSelected ? SELECTED_BG : UNSELECTED_BG);
        g.fillRect(x, y, w, h);
      }
      @Override protected void paintContentBorderRightEdge(
          Graphics g, int tabPlacement, int selectedIndex,
          int x, int y, int w, int h) {
        g.setColor(SELECTED_BG);
        g.fillRect(x, y, w, h);
      }
      // ...
    });
    setOpaque(true);
    setForeground(Color.WHITE);
    setTabPlacement(SwingConstants.LEFT);
    setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
  }
};

References

2017/01/27

aterai

Change the tab shape of JTabbedPane to trapezoid

Code

class IsoscelesTrapezoidTabbedPaneUI extends BasicTabbedPaneUI {
  private static final int ADJ2 = 3;
  private final Color selectedTabColor = UIManager.getColor("TabbedPane.selected");
  private final Color tabBackgroundColor = Color.LIGHT_GRAY;
  private final Color tabBorderColor = Color.GRAY;

  @Override protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { //NOPMD
    int tabCount = tabPane.getTabCount();

    Rectangle iconRect = new Rectangle(),
    textRect = new Rectangle();
    Rectangle clipRect = g.getClipBounds();

    for (int i = runCount - 1; i >= 0; i--) {
      int start = tabRuns[i];
      int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
      int end = next != 0 ? next - 1 : tabCount - 1; //NOPMD
      // for (int j = start; j <= end; j++) {
      // https://stackoverflow.com/questions/41566659/tabs-rendering-order-in-custom-jtabbedpane
      for (int j = end; j >= start; j--) {
        if (j != selectedIndex && rects[j].intersects(clipRect)) {
          paintTab(g, tabPlacement, rects, j, iconRect, textRect);
        }
      }
    }
    if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
      paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
    }
  }
  @Override protected void paintTabBorder(
      Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
    // Do nothing
  }
  @Override protected void paintFocusIndicator(
      Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex,
      Rectangle iconRect, Rectangle textRect, boolean isSelected) {
    // Do nothing
  }
  @Override protected void paintContentBorderTopEdge(
      Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
    super.paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
    Rectangle selRect = getTabBounds(selectedIndex, calcRect);
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setColor(selectedTabColor);
    g2.drawLine(selRect.x - ADJ2 + 1, y, selRect.x + selRect.width + ADJ2 - 1, y);
    g2.dispose();
  }
  @Override protected void paintTabBackground(
      Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    int textShiftOffset = isSelected ? 0 : 1;

    Rectangle clipRect = g2.getClipBounds();
    clipRect.grow(ADJ2 + 1, 0);
    g2.setClip(clipRect);

    GeneralPath trapezoid = new GeneralPath();
    trapezoid.moveTo(x - ADJ2,     y + h);
    trapezoid.lineTo(x + ADJ2,     y + textShiftOffset);
    trapezoid.lineTo(x + w - ADJ2, y + textShiftOffset);
    trapezoid.lineTo(x + w + ADJ2, y + h);
    //trapezoid.closePath();

    g2.setColor(isSelected ? selectedTabColor : tabBackgroundColor);
    g2.fill(trapezoid);

    g2.setColor(tabBorderColor);
    g2.draw(trapezoid);

    g2.dispose();
  }
}

References

2012/01/25

aterai

Sharing tabs between 2 JFrames

Code

//Display "DropLocation" using JLayer 
class DropLocationLayerUI extends LayerUI {
  private static final int LINEWIDTH = 3;
  private final Rectangle lineRect = new Rectangle();
  @Override public void paint(Graphics g, JComponent c) {
    super.paint (g, c);
    JLayer layer = (JLayer)c;
    DnDTabbedPane tabbedPane = (DnDTabbedPane)layer.getView();
    DnDTabbedPane.DropLocation loc = tabbedPane.getDropLocation();
    if(loc != null && loc.isDropable() && loc.getIndex()>=0) {
      int index = loc.getIndex();
      boolean isZero = index==0;
      Rectangle r = tabbedPane.getBoundsAt(isZero?0:index-1);
      if(tabbedPane.getTabPlacement()==JTabbedPane.TOP ||
         tabbedPane.getTabPlacement()==JTabbedPane.BOTTOM) {
        lineRect.setRect(
            r.x-LINEWIDTH/2+r.width*(isZero?0:1), r.y,LINEWIDTH,r.height);
      }else{
        lineRect.setRect(
            r.x,r.y-LINEWIDTH/2+r.height*(isZero?0:1), r.width,LINEWIDTH);
      }
      Graphics2D g2 = (Graphics2D)g.create();
      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
      g2.setColor(Color.RED);
      g2.fill(lineRect);
      g2.dispose();
    }
  }
}
private final JLabel label = new JLabel() {
// https://free-the-pixel.blogspot.com/2010/04/ghost-drag-and-drop-over-multiple.html
// Free the pixel: GHOST drag and drop, over multiple windows]
  @Override public boolean contains(int x, int y) {
    return false;
  }
};
private final JWindow dialog = new JWindow();
public TabTransferHandler() {
  dialog.add(label);
  //dialog.setAlwaysOnTop(true); // Web Start
  dialog.setOpacity(0.5f);
  //com.sun.awt.AWTUtilities.setWindowOpacity(dialog, 0.5f); // JDK 1.6.0
  DragSource.getDefaultDragSource().addDragSourceMotionListener(
      new DragSourceMotionListener() {
    @Override public void dragMouseMoved(DragSourceDragEvent dsde) {
      Point pt = dsde.getLocation();
      pt.translate(5, 5); // offset
      dialog.setLocation(pt);
    }
  });
//...

References

2010/04/05

aterai

JTabbedPane selected tab height

Code

tabbedPane.setUI(new com.sun.java.swing.plaf.windows.WindowsTabbedPaneUI() {
  @Override protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
    return 32;
  }
  @Override protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
                                    int tabIndex, Rectangle iconRect, Rectangle textRect) {
    Rectangle tabRect  = rects[tabIndex];
    int selectedIndex  = tabPane.getSelectedIndex();
    boolean isSelected = selectedIndex == tabIndex;
    if(!isSelected) {
      //JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT
      rects[tabIndex].y = 16;
      rects[tabIndex].height = 16;
    }
    super.paintTab(g,tabPlacement,rects,tabIndex,iconRect,textRect);
  }
});

References

2010/02/25

aterai

TabTransferHandler

Code

class TabTransferHandler extends TransferHandler {
  private final DataFlavor localObjectFlavor;
  public TabTransferHandler() {
    System.out.println("TabTransferHandler");
    localObjectFlavor = new DataFlavor(DnDTabData.class, "DnDTabData");
  }
  private DnDTabbedPane source = null;
  @Override protected Transferable createTransferable(JComponent c) {
    System.out.println("createTransferable");
    if (c instanceof DnDTabbedPane) source = (DnDTabbedPane) c;
    return new Transferable() {
      @Override public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[] {localObjectFlavor};
      }
      @Override public boolean isDataFlavorSupported(DataFlavor flavor) {
        return Objects.equals(localObjectFlavor, flavor);
      }
      @Override public Object getTransferData(DataFlavor flavor) 
            throws UnsupportedFlavorException, IOException {
        if (isDataFlavorSupported(flavor)) {
          return new DnDTabData(source);
        } else {
           throw new UnsupportedFlavorException(flavor);
        }
      }
    };
  }
  @Override public boolean canImport(TransferSupport support) {
    //System.out.println("canImport");
    if (!support.isDrop() || !support.isDataFlavorSupported(localObjectFlavor)) {
      return false;
    }
    support.setDropAction(TransferHandler.MOVE);
    DropLocation tdl = support.getDropLocation();
    Point pt = tdl.getDropPoint();
    DnDTabbedPane target = (DnDTabbedPane) support.getComponent();
    target.autoScrollTest(pt);
    DnDTabbedPane.DropLocation dl =
      (DnDTabbedPane.DropLocation) target.dropLocationForPoint(pt);
    int idx = dl.getIndex();
    boolean isDroppable = false;

    if (target == source) {
      isDroppable = target.getTabAreaBounds().contains(pt) && idx >= 0 &&
                   idx != target.dragTabIndex && idx != target.dragTabIndex + 1;
    } else {
      if (source != null && target != source.getComponentAt(source.dragTabIndex)) {
        isDroppable = target.getTabAreaBounds().contains(pt) && idx >= 0;
      }
    }

    Component c = target.getRootPane().getGlassPane();
    c.setCursor(isDroppable?DragSource.DefaultMoveDrop:DragSource.DefaultMoveNoDrop);
    if (isDroppable) {
      support.setShowDropLocation(true);
      dl.setDroppable(true);
      target.setDropLocation(dl, null, true);
      return true;
    } else {
      support.setShowDropLocation(false);
      dl.setDroppable(false);
      target.setDropLocation(dl, null, false);
      return false;
    }
  }

  private BufferedImage makeDragTabImage(DnDTabbedPane tabbedPane) {
    Rectangle rect = tabbedPane.getBoundsAt(tabbedPane.dragTabIndex);
    BufferedImage image = new BufferedImage(
      tabbedPane.getWidth(), tabbedPane.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics g = image.getGraphics();
    tabbedPane.paint(g);
    g.dispose();
    if (rect.x < 0) {
      rect.translate(-rect.x, 0);
    }
    if (rect.y < 0) {
      rect.translate(0, -rect.y);
    }
    if (rect.x + rect.width > image.getWidth()) {
      rect.width = image.getWidth() - rect.x;
    }
    if (rect.y + rect.height > image.getHeight()) {
      rect.height = image.getHeight() - rect.y;
    }
    return image.getSubimage(rect.x, rect.y, rect.width, rect.height);
  }

  private static GhostGlassPane glassPane;
  @Override public int getSourceActions(JComponent c) {
    System.out.println("getSourceActions");
    DnDTabbedPane src = (DnDTabbedPane) c;
    if (glassPane == null) {
      c.getRootPane().setGlassPane(glassPane = new GhostGlassPane(src));
    }
    if (src.dragTabIndex < 0) return TransferHandler.NONE;
    glassPane.setImage(makeDragTabImage(src));
    source = src;
    //setDragImage(img); //java 1.7.0-ea-b84
    glassPane.setVisible(true);
    return TransferHandler.MOVE;
  }
  @Override public boolean importData(TransferSupport support) {
    System.out.println("importData");
    if (!canImport(support)) return false;

    DnDTabbedPane target = (DnDTabbedPane) support.getComponent();
    DnDTabbedPane.DropLocation dl = target.getDropLocation();
    try {
      DnDTabbedPane source = (DnDTabbedPane) support.getTransferable()
        .getTransferData(localObjectFlavor);
      int index = dl.getIndex(); //boolean insert = dl.isInsert();
      if (target == source) {
        source.convertTab(source.dragTabIndex, index);
      } else {
        source.exportTab(source.dragTabIndex, target, index);
      }
      return true;
    } catch (UnsupportedFlavorException ufe) {
      ufe.printStackTrace();
    } catch (IOException ioe) {
      ioe.printStackTrace();
    }
    return false;
  }
  @Override protected void exportDone(JComponent c, Transferable data, int action) {
    System.out.println("exportDone");
    DnDTabbedPane src = (DnDTabbedPane) c;
    c.getRootPane().getGlassPane().setVisible(false);
    src.setDropLocation(null, null, false);
  }
}

References

2009/06/10

aterai

New Tab Button

Code

class TabLayout implements LayoutManager, java.io.Serializable {
  public void addLayoutComponent(String name, Component comp) {}
  public void removeLayoutComponent(Component comp) {}
  public Dimension preferredLayoutSize(Container parent) {
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int last = parent.getComponentCount()-1;
      int w = 0, h = 0;
      if(last >= 0) {
        Component comp = parent.getComponent(last);
        Dimension d = comp.getPreferredSize();
        w = d.width;
        h = d.height;
      }
      return new Dimension(insets.left + insets.right + w,
                           insets.top + insets.bottom + h);
    }
  }

  public Dimension minimumLayoutSize(Container parent) {
    synchronized (parent.getTreeLock()) {
      return new Dimension(100, 24);
    }
  }

  public void layoutContainer(Container parent) {
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int ncomponents = parent.getComponentCount();
      int nrows = 1;
      int ncols = ncomponents-1;
      //boolean ltr = parent.getComponentOrientation().isLeftToRight();

      if (ncomponents == 0) {
        return;
      }
      int lastw = parent.getComponent(ncomponents-1).getPreferredSize().width;
      int width = parent.getWidth() - (insets.left + insets.right) - lastw;
      int h = parent.getHeight() - (insets.top + insets.bottom);
      int w = (width > 100*(ncomponents-1))?100:width/ncols;
      int gap = width - w*ncols;
      int x = insets.left;
      int y = insets.top;
      for (int i=0;i < ncomponents;i++) {
        int a = (gap > 0)?1:0;
        gap--;
        int cw = (i==ncols)?lastw:w+a;
        parent.getComponent(i).setBounds(x, y, cw, h);
        x += w + a;
      }
    }
  }
  public String toString() {
    return getClass().getName();
  }
}

References

2008/11/11

aterai

create JTabbedPane like component using CardLayout and JRadioButton(or JTableHeader)

Code

class CardLayoutTabbedPane extends JPanel {
  protected final CardLayout cardLayout = new CardLayout();
  protected final JPanel tabPanel = new JPanel(new GridLayout(1, 0, 0, 0));
  protected final JPanel wrapPanel = new JPanel(new BorderLayout(0, 0));
  protected final JPanel contentsPanel = new JPanel(cardLayout);
  protected final ButtonGroup bg = new ButtonGroup();
  public CardLayoutTabbedPane() {
    super(new BorderLayout());
    int left  = 1;
    int right = 3;
    tabPanel.setBorder(BorderFactory.createEmptyBorder(1,left,0,right));
    contentsPanel.setBorder(BorderFactory.createEmptyBorder(4,left,2,right));
    wrapPanel.add(tabPanel);
    wrapPanel.add(new JLabel("test:"), BorderLayout.WEST);
    add(wrapPanel, BorderLayout.NORTH);
    add(contentsPanel);
  }
  public void addTab(final String title, final Component comp) {
    JRadioButton b = new TabButton(new AbstractAction(title) {
      public void actionPerformed(ActionEvent e) {
        cardLayout.show(contentsPanel, title);
      }
    });
    tabPanel.add(b);
    bg.add(b);
    b.setSelected(true);
    contentsPanel.add(comp, title);
    cardLayout.show(contentsPanel, title);
  }
}

References

2008/09/11

aterai

Double-click on each tab and change its name like Excel

Code

private Component tabComponent = null;
private int editing_idx = -1;
private int len = -1;
private Dimension dim;
private void startEditing() {
  //System.out.println("start");
  editing_idx  = tabbedPane.getSelectedIndex();
  tabComponent = tabbedPane.getTabComponentAt(editing_idx);
  tabbedPane.setTabComponentAt(editing_idx, editor);
  editor.setVisible(true);
  editor.setText(tabbedPane.getTitleAt(editing_idx));
  editor.selectAll();
  editor.requestFocusInWindow();
  len = editor.getText().length();
  dim = editor.getPreferredSize();
  editor.setMinimumSize(dim);
}
private void cancelEditing() {
  //System.out.println("cancel");
  if(editing_idx>=0) {
    tabbedPane.setTabComponentAt(editing_idx, tabComponent);
    editor.setVisible(false);
    editing_idx = -1;
    len = -1;
    tabComponent = null;
    editor.setPreferredSize(null);
  }
}
private void renameTabTitle() {
  //System.out.println("rename");
  String title = editor.getText().trim();
  if(editing_idx>=0 && !title.isEmpty()) {
    tabbedPane.setTitleAt(editing_idx, title);
  }
  cancelEditing();
}

References

2008/04/07

aterai

Drag and Drop the Tabs in JTabbedPane

Code

class DnDTabbedPane extends JTabbedPane {
  private static final int LINEWIDTH = 3;
  private static final String NAME = "test";
  private final GhostGlassPane glassPane = new GhostGlassPane();
  private final Rectangle lineRect  = new Rectangle();
  private final Color   lineColor = new Color(0, 100, 255);
  private int dragTabIndex = -1;

  private void clickArrowButton(String actionKey) {
    ActionMap map = getActionMap();
    if(map != null) {
      Action action = map.get(actionKey);
      if (action != null && action.isEnabled()) {
        action.actionPerformed(new ActionEvent(
            this, ActionEvent.ACTION_PERFORMED, null, 0, 0));
      }
    }
  }
  private static Rectangle rBackward = new Rectangle();
  private static Rectangle rForward  = new Rectangle();
  private static int rwh = 20;
  private static int buttonsize = 30;//XXX: magic number of scroll button size
  private void autoScrollTest(Point glassPt) {
    Rectangle r = getTabAreaBounds();
    int tabPlacement = getTabPlacement();
    if(tabPlacement==TOP || tabPlacement==BOTTOM) {
      rBackward.setBounds(r.x, r.y, rwh, r.height);
      rForward.setBounds(
          r.x+r.width-rwh-buttonsize, r.y, rwh+buttonsize, r.height);
    }else if(tabPlacement==LEFT || tabPlacement==RIGHT) {
      rBackward.setBounds(r.x, r.y, r.width, rwh);
      rForward.setBounds(
          r.x, r.y+r.height-rwh-buttonsize, r.width, rwh+buttonsize);
    }
    rBackward = SwingUtilities.convertRectangle(
        getParent(), rBackward, glassPane);
    rForward  = SwingUtilities.convertRectangle(
        getParent(), rForward,  glassPane);
    if(rBackward.contains(glassPt)) {
      //System.out.println(new java.util.Date() + "Backward");
      clickArrowButton("scrollTabsBackwardAction");
    }else if(rForward.contains(glassPt)) {
      //System.out.println(new java.util.Date() + "Forward");
      clickArrowButton("scrollTabsForwardAction");
    }
  }
  public DnDTabbedPane() {
    super();
    final DragSourceListener dsl = new DragSourceListener() {
      @Override public void dragEnter(DragSourceDragEvent e) {
        e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
      }
      @Override public void dragExit(DragSourceEvent e) {
        e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
        lineRect.setRect(0,0,0,0);
        glassPane.setPoint(new Point(-1000,-1000));
        glassPane.repaint();
      }
      @Override public void dragOver(DragSourceDragEvent e) {
        Point glassPt = e.getLocation();
        SwingUtilities.convertPointFromScreen(glassPt, glassPane);
        int targetIdx = getTargetTabIndex(glassPt);
        //if(getTabAreaBounds().contains(tabPt) && targetIdx>=0 &&
        if(getTabAreaBounds().contains(glassPt) && targetIdx>=0 &&
           targetIdx!=dragTabIndex && targetIdx!=dragTabIndex+1) {
          e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
          glassPane.setCursor(DragSource.DefaultMoveDrop);
        }else{
          e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
          glassPane.setCursor(DragSource.DefaultMoveNoDrop);
        }
      }
      @Override public void dragDropEnd(DragSourceDropEvent e) {
        lineRect.setRect(0,0,0,0);
        dragTabIndex = -1;
        glassPane.setVisible(false);
        if(hasGhost()) {
          glassPane.setVisible(false);
          glassPane.setImage(null);
        }
      }
      @Override public void dropActionChanged(DragSourceDragEvent e) {}
    };
    final Transferable t = new Transferable() {
      private final DataFlavor FLAVOR = new DataFlavor(
          DataFlavor.javaJVMLocalObjectMimeType, NAME);
      @Override public Object getTransferData(DataFlavor flavor) {
        return DnDTabbedPane.this;
      }
      @Override public DataFlavor[] getTransferDataFlavors() {
        DataFlavor[] f = new DataFlavor[1];
        f[0] = this.FLAVOR;
        return f;
      }
      @Override public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor.getHumanPresentableName().equals(NAME);
      }
    };
    final DragGestureListener dgl = new DragGestureListener() {
      @Override public void dragGestureRecognized(DragGestureEvent e) {
        if(getTabCount() <= 1) return;
        Point tabPt = e.getDragOrigin();
        dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
        //"disabled tab problem".
        if(dragTabIndex < 0 || !isEnabledAt(dragTabIndex)) return;
        initGlassPane(e.getComponent(), e.getDragOrigin());
        try{
          e.startDrag(DragSource.DefaultMoveDrop, t, dsl);
        }catch(InvalidDnDOperationException idoe) {
          idoe.printStackTrace();
        }
      }
    };
    new DropTarget(glassPane, DnDConstants.ACTION_COPY_OR_MOVE,
                   new CDropTargetListener(), true);
    new DragSource().createDefaultDragGestureRecognizer(
          this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
  }

  class CDropTargetListener implements DropTargetListener{
    @Override public void dragEnter(DropTargetDragEvent e) {
      if(isDragAcceptable(e)) e.acceptDrag(e.getDropAction());
      else e.rejectDrag();
    }
    @Override public void dragExit(DropTargetEvent e) {}
    @Override public void dropActionChanged(DropTargetDragEvent e) {}

    private Point _glassPt = new Point();
    @Override public void dragOver(final DropTargetDragEvent e) {
      Point glassPt = e.getLocation();
      if(getTabPlacement()==JTabbedPane.TOP ||
         getTabPlacement()==JTabbedPane.BOTTOM) {
        initTargetLeftRightLine(getTargetTabIndex(glassPt));
      }else{
        initTargetTopBottomLine(getTargetTabIndex(glassPt));
      }
      if(hasGhost()) {
        glassPane.setPoint(glassPt);
      }
      if(!_glassPt.equals(glassPt)) glassPane.repaint();
      _glassPt = glassPt;
      autoScrollTest(glassPt);
    }

    @Override public void drop(DropTargetDropEvent e) {
      if(isDropAcceptable(e)) {
        convertTab(dragTabIndex, getTargetTabIndex(e.getLocation()));
        e.dropComplete(true);
      }else{
        e.dropComplete(false);
      }
      repaint();
    }
    private boolean isDragAcceptable(DropTargetDragEvent e) {
      Transferable t = e.getTransferable();
      if(t==null) return false;
      DataFlavor[] f = e.getCurrentDataFlavors();
      if(t.isDataFlavorSupported(f[0]) && dragTabIndex>=0) {
        return true;
      }
      return false;
    }
    private boolean isDropAcceptable(DropTargetDropEvent e) {
      Transferable t = e.getTransferable();
      if(t==null) return false;
      DataFlavor[] f = t.getTransferDataFlavors();
      if(t.isDataFlavorSupported(f[0]) && dragTabIndex>=0) {
        return true;
      }
      return false;
    }
  }

  private boolean hasGhost = true;
  public void setPaintGhost(boolean flag) {
    hasGhost = flag;
  }
  public boolean hasGhost() {
    return hasGhost;
  }
  private boolean isPaintScrollArea = true;
  public void setPaintScrollArea(boolean flag) {
    isPaintScrollArea = flag;
  }
  public boolean isPaintScrollArea() {
    return isPaintScrollArea;
  }

  private int getTargetTabIndex(Point glassPt) {
    Point tabPt = SwingUtilities.convertPoint(
        glassPane, glassPt, DnDTabbedPane.this);
    boolean isTB = getTabPlacement()==JTabbedPane.TOP ||
                   getTabPlacement()==JTabbedPane.BOTTOM;
    for(int i=0;i < getTabCount();i++) {
      Rectangle r = getBoundsAt(i);
      if(isTB) r.setRect(r.x-r.width/2, r.y,  r.width, r.height);
      else   r.setRect(r.x, r.y-r.height/2, r.width, r.height);
      if(r.contains(tabPt)) return i;
    }
    Rectangle r = getBoundsAt(getTabCount()-1);
    if(isTB) r.setRect(r.x+r.width/2, r.y,  r.width, r.height);
    else   r.setRect(r.x, r.y+r.height/2, r.width, r.height);
    return   r.contains(tabPt)?getTabCount():-1;
  }
  private void convertTab(int prev, int next) {
    if(next < 0 || prev==next) {
      return;
    }
    Component cmp = getComponentAt(prev);
    Component tab = getTabComponentAt(prev);
    String str  = getTitleAt(prev);
    Icon icon   = getIconAt(prev);
    String tip  = getToolTipTextAt(prev);
    boolean flg   = isEnabledAt(prev);
    int tgtindex  = prev>next ? next : next-1;
    remove(prev);
    insertTab(str, icon, cmp, tip, tgtindex);
    setEnabledAt(tgtindex, flg);
    //When you drag'n'drop a disabled tab, it finishes enabled and selected.
    //pointed out by dlorde
    if(flg) setSelectedIndex(tgtindex);

    //I have a component in all tabs (jlabel with an X to close the tab)
    //and when i move a tab the component disappear.
    //pointed out by Daniel Dario Morales Salas
    setTabComponentAt(tgtindex, tab);
  }

  private void initTargetLeftRightLine(int next) {
    if(next < 0 || dragTabIndex==next || next-dragTabIndex==1) {
      lineRect.setRect(0,0,0,0);
    }else if(next==0) {
      Rectangle r = SwingUtilities.convertRectangle(
          this, getBoundsAt(0), glassPane);
      lineRect.setRect(r.x-LINEWIDTH/2,r.y,LINEWIDTH,r.height);
    }else{
      Rectangle r = SwingUtilities.convertRectangle(
          this, getBoundsAt(next-1), glassPane);
      lineRect.setRect(r.x+r.width-LINEWIDTH/2,r.y,LINEWIDTH,r.height);
    }
  }
  private void initTargetTopBottomLine(int next) {
    if(next < 0 || dragTabIndex==next || next-dragTabIndex==1) {
      lineRect.setRect(0,0,0,0);
    }else if(next==0) {
      Rectangle r = SwingUtilities.convertRectangle(
          this, getBoundsAt(0), glassPane);
      lineRect.setRect(r.x,r.y-LINEWIDTH/2,r.width,LINEWIDTH);
    }else{
      Rectangle r = SwingUtilities.convertRectangle(
          this, getBoundsAt(next-1), glassPane);
      lineRect.setRect(r.x,r.y+r.height-LINEWIDTH/2,r.width,LINEWIDTH);
    }
  }

  private void initGlassPane(Component c, Point tabPt) {
    getRootPane().setGlassPane(glassPane);
    if(hasGhost()) {
      Rectangle rect = getBoundsAt(dragTabIndex);
      BufferedImage image = new BufferedImage(
          c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
      Graphics g = image.getGraphics();
      c.paint(g);
      rect.x = rect.x < 0?0:rect.x;
      rect.y = rect.y < 0?0:rect.y;
      image = image.getSubimage(rect.x,rect.y,rect.width,rect.height);
      glassPane.setImage(image);
    }
    Point glassPt = SwingUtilities.convertPoint(c, tabPt, glassPane);
    glassPane.setPoint(glassPt);
    glassPane.setVisible(true);
  }

  private Rectangle getTabAreaBounds() {
    Rectangle tabbedRect = getBounds();
    //pointed out by daryl. NullPointerException: i.e. addTab("Tab",null)
    //Rectangle compRect   = getSelectedComponent().getBounds();
    Component comp = getSelectedComponent();
    int idx = 0;
    while(comp==null && idx < getTabCount()) comp = getComponentAt(idx++);
    Rectangle compRect = (comp==null)?new Rectangle():comp.getBounds();
    int tabPlacement = getTabPlacement();
    if(tabPlacement==TOP) {
      tabbedRect.height = tabbedRect.height - compRect.height;
    }else if(tabPlacement==BOTTOM) {
      tabbedRect.y = tabbedRect.y + compRect.y + compRect.height;
      tabbedRect.height = tabbedRect.height - compRect.height;
    }else if(tabPlacement==LEFT) {
      tabbedRect.width = tabbedRect.width - compRect.width;
    }else if(tabPlacement==RIGHT) {
      tabbedRect.x = tabbedRect.x + compRect.x + compRect.width;
      tabbedRect.width = tabbedRect.width - compRect.width;
    }
    tabbedRect.grow(2, 2);
    return tabbedRect;
  }
  class GhostGlassPane extends JPanel {
    private final AlphaComposite composite;
    private Point location = new Point(0, 0);
    private BufferedImage draggingGhost = null;
    public GhostGlassPane() {
      setOpaque(false);
      composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
      //[JDK-6700748] Cursor flickering during D&D when using CellRendererPane with validation - Java Bug System
      //https://bugs.openjdk.java.net/browse/JDK-6700748
      //setCursor(null);
    }
    public void setImage(BufferedImage draggingGhost) {
      this.draggingGhost = draggingGhost;
    }
    public void setPoint(Point location) {
      this.location = location;
    }
    @Override public void paintComponent(Graphics g) {
      Graphics2D g2 = (Graphics2D) g;
      g2.setComposite(composite);
      if(isPaintScrollArea() && getTabLayoutPolicy()==SCROLL_TAB_LAYOUT) {
        g2.setPaint(Color.RED);
        g2.fill(rBackward);
        g2.fill(rForward);
      }
      if(draggingGhost != null) {
        double xx = location.getX() - (draggingGhost.getWidth(this) /2d);
        double yy = location.getY() - (draggingGhost.getHeight(this)/2d);
        g2.drawImage(draggingGhost, (int)xx, (int)yy , null);
      }
      if(dragTabIndex>=0) {
        g2.setPaint(lineColor);
        g2.fill(lineRect);
      }
    }
  }
}

References

2008/03/21

aterai

Horizontally fill tabs of a JTabbedPane

Code

//horizontal filling, same width tabs
class ClippedTitleTabbedPane extends JTabbedPane {
  public ClippedTitleTabbedPane() {
    super();
  }
  public ClippedTitleTabbedPane(int tabPlacement) {
    super(tabPlacement);
  }
  private Insets getTabInsets() {
    Insets i = UIManager.getInsets("TabbedPane.tabInsets");
    if(i!=null) {
      return i;
    }else{
      SynthStyle style = SynthLookAndFeel.getStyle(
        this, Region.TABBED_PANE_TAB);
      SynthContext context = new SynthContext(
        this, Region.TABBED_PANE_TAB, style, SynthConstants.ENABLED);
      return style.getInsets(context, null);
    }
  }
  private Insets getTabAreaInsets() {
    Insets i = UIManager.getInsets("TabbedPane.tabAreaInsets");
    if(i!=null) {
      return i;
    }else{
      SynthStyle style = SynthLookAndFeel.getStyle(
        this, Region.TABBED_PANE_TAB_AREA);
      SynthContext context = new SynthContext(
        this, Region.TABBED_PANE_TAB_AREA, style, SynthConstants.ENABLED);
      return style.getInsets(context, null);
    }
  }
  @Override public void doLayout() {
    int tabCount  = getTabCount();
    if(tabCount==0) return;
    Insets tabInsets   = getTabInsets();
    Insets tabAreaInsets = getTabAreaInsets();
    Insets insets = getInsets();
    int areaWidth = getWidth() - tabAreaInsets.left - tabAreaInsets.right
                               - insets.left        - insets.right;
    int tabWidth  = 0; // = tabInsets.left + tabInsets.right + 3;
    int gap     = 0;

    switch(getTabPlacement()) {
      case LEFT: case RIGHT:
      tabWidth = areaWidth / 4;
      gap = 0;
      break;
      case BOTTOM: case TOP: default:
      tabWidth = areaWidth / tabCount;
      gap = areaWidth - (tabWidth * tabCount);
    }
    // "3" is magic number @see BasicTabbedPaneUI#calculateTabWidth
    tabWidth = tabWidth - tabInsets.left - tabInsets.right - 3;
    for(int i=0;i < tabCount;i++) {
      JLabel l = (JLabel)getTabComponentAt(i);
      if(l==null) break;
      l.setPreferredSize(new Dimension(
          tabWidth+(i < gap?1:0), l.getPreferredSize().height));
    }
    super.doLayout();
  }
  @Override
  public void insertTab(
      String title, Icon icon, Component component,
      String tip, int index) {
    super.insertTab(title, icon, component,
                    tip==null?title:tip, index);
    JLabel label = new JLabel(title, JLabel.CENTER);
    Dimension dim = label.getPreferredSize();
    Insets tabInsets = getTabInsets();
    label.setPreferredSize(new Dimension(
      0, dim.height+tabInsets.top+tabInsets.bottom));
    setTabComponentAt(index, label);
  }
}

References