/*
* SpinningTabbedPane.java
*
* Copyright 2002, 2003 (C) B. K. Oxley (binkley) <binkley@alumni.rice.edu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Created on August 18th, 2002.
*/
package pcgen.gui2.tools; // hm.binkley.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.plaf.TabbedPaneUI;
import pcgen.system.LanguageBundle;
import pcgen.util.Logging;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* {@code SpinningTabbedPane}.
*
* @author <a href="binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
*
* @see JTabbedPane
*/
public class SpinningTabbedPane extends JTabbedPane
{
private static final long serialVersionUID = 4980035692406423131L;
private static final int PLACE_OFFSET = 0;
private static final int MOVE_LEFT_RIGHT_OFFSET = 4;
private static final int MOVE_UP_DOWN_OFFSET = 8;
private static final int GROUP_OFFSET = 12;
private static final int TAB_OFFSET = 16;
private static final int UNGROUP_CHILD_OFFSET = 20;
private static final int UNGROUP_SELF_OFFSET = 24;
private static final int UNGROUP_SINGLE_OFFSET = 28;
private static final String[] labels =
{
LanguageBundle.getString("in_top"), // place
LanguageBundle.getString("in_left"),
LanguageBundle.getString("in_bottom"),
LanguageBundle.getString("in_right"),
LanguageBundle.getString("in_beginning"), // move left/right
LanguageBundle.getString("in_left"),
LanguageBundle.getString("in_end"),
LanguageBundle.getString("in_right"),
LanguageBundle.getString("in_top"), // move up/down
LanguageBundle.getString("in_up"),
LanguageBundle.getString("in_bottom"),
LanguageBundle.getString("in_down"),
LanguageBundle.getString("in_up"), // group
LanguageBundle.getString("in_left"),
LanguageBundle.getString("in_down"),
LanguageBundle.getString("in_right"),
null, // tab
null,
null,
null,
LanguageBundle.getString("in_ungroupTop"), // ungroup child
LanguageBundle.getString("in_ungroupLeft"),
LanguageBundle.getString("in_ungroupBottom"),
LanguageBundle.getString("in_ungroupRight"),
LanguageBundle.getString("in_ungroupTop"), // ungroup self
LanguageBundle.getString("in_ungroupLeft"),
LanguageBundle.getString("in_ungroupBottom"),
LanguageBundle.getString("in_ungroupRight"),
LanguageBundle.getString("in_ungroupUp"), // ungroup single
LanguageBundle.getString("in_ungroupLeft"),
LanguageBundle.getString("in_ungroupDown"),
LanguageBundle.getString("in_ungroupRight")
};
private static final ImageIcon[] icons =
{
Utilities.UP_ICON, // place
Utilities.LEFT_ICON,
Utilities.DOWN_ICON,
Utilities.RIGHT_ICON,
Utilities.BEGINNING_ICON, // move left/right
Utilities.LEFT_ICON,
Utilities.END_ICON,
Utilities.RIGHT_ICON,
Utilities.TOP_ICON, // move up/down
Utilities.UP_ICON,
Utilities.BOTTOM_ICON,
Utilities.DOWN_ICON,
Utilities.UP_ICON, // group
Utilities.LEFT_ICON,
Utilities.DOWN_ICON,
Utilities.RIGHT_ICON,
Utilities.UP_ICON, // tab
Utilities.LEFT_ICON,
Utilities.DOWN_ICON,
Utilities.RIGHT_ICON,
Utilities.TOP_ICON, // ungroup child
Utilities.BEGINNING_ICON,
Utilities.BOTTOM_ICON,
Utilities.END_ICON,
Utilities.TOP_ICON, // ungroup self
Utilities.BEGINNING_ICON,
Utilities.BOTTOM_ICON,
Utilities.END_ICON,
Utilities.UP_ICON, // ungroup single
Utilities.LEFT_ICON,
Utilities.DOWN_ICON,
Utilities.RIGHT_ICON
};
private static final String[] tips =
{
LanguageBundle.getString("in_spinTips1"),
LanguageBundle.getString("in_spinTips2"),
LanguageBundle.getString("in_spinTips3"),
LanguageBundle.getString("in_spinTips4"),
LanguageBundle.getString("in_spinTips5"),
LanguageBundle.getString("in_spinTips6"),
LanguageBundle.getString("in_spinTips7"),
LanguageBundle.getString("in_spinTips8"),
LanguageBundle.getString("in_spinTips9"),
LanguageBundle.getString("in_spinTips10"),
LanguageBundle.getString("in_spinTips11"),
LanguageBundle.getString("in_spinTips12"),
LanguageBundle.getString("in_spinTips13"),
LanguageBundle.getString("in_spinTips14"),
LanguageBundle.getString("in_spinTips15"),
LanguageBundle.getString("in_spinTips16"),
LanguageBundle.getString("in_spinTips17"),
LanguageBundle.getString("in_spinTips18"),
LanguageBundle.getString("in_spinTips19"),
LanguageBundle.getString("in_spinTips20"),
LanguageBundle.getString("in_spinTips21"),
LanguageBundle.getString("in_spinTips22"),
LanguageBundle.getString("in_spinTips23"),
LanguageBundle.getString("in_spinTips24"),
LanguageBundle.getString("in_spinTips25"),
LanguageBundle.getString("in_spinTips26"),
LanguageBundle.getString("in_spinTips27"),
LanguageBundle.getString("in_spinTips28"),
LanguageBundle.getString("in_spinTips29"),
LanguageBundle.getString("in_spinTips30"),
LanguageBundle.getString("in_spinTips31"),
LanguageBundle.getString("in_spinTips32")
};
private final PopupMenuPolicy policy = new DefaultPopupMenuPolicy();
private final Set<Component> locked = new HashSet<>();
@Nullable
private SpinningTabbedPane parent = null;
public SpinningTabbedPane()
{
addMouseListener(new PopupListener());
}
@Override
public final void setTabPlacement(int placement)
{
super.setTabPlacement(placement);
if (parent != null)
{
parent.updateTabUIAt(parent.indexOfComponent(this));
}
}
@Override
public final void setTitleAt(int index, @Nullable String title)
{
String extra = getExtraTitleAt(index);
if (extra != null)
{
if ((title == null) || (title.isEmpty()))
{
title = extra;
}
else
{
// Separate extra from the title with one space.
title += (" " + extra);
}
}
super.setTitleAt(index, title);
}
/**
* Returns the tab index corresponding to the tab whose bounds
* intersect the specified location. Returns -1 if no tab
* intersects the location.
* NB: This method provides to JDK1.3 the JTabbedPane.indexAtLocation
* method first provided in JDK 1.4. The method interface cannot be
* changed.
*
* Must be public in order to overrride javax.swing.JTabbedPane.
*
* @param x the x location relative to this tabbedpane
* @param y the y location relative to this tabbedpane
* @return the tab index which intersects the location, or
* -1 if no tab intersects the location
*/
@Override
public final int indexAtLocation(int x, int y)
{
if (ui != null)
{
return ((TabbedPaneUI) ui).tabForCoordinate(this, x, y);
}
return -1;
}
private static void setMenuItem(@NotNull JMenuItem menuItem, int offset)
{
String label = labels[offset];
menuItem.setText(label);
if (label != null)
{
menuItem.setMnemonic(label.charAt(0));
}
menuItem.setIcon(icons[offset]);
menuItem.setToolTipText(tips[offset]);
}
// Need to use action events instead XXX
@NotNull
private static SpinningTabbedPane createPane()
{
return new SpinningTabbedPane();
}
private static int offsetForPlacement(int placement)
{
return placement - 1;
}
private static int placementForSlot(int slot, int placement)
{
return ((placement - 1 + slot) % 4) + 1;
}
private String getExtraTitleAt(int index)
{
Component c = getComponentAt(index);
return (c instanceof SpinningTabbedPane) ? ("(" +
((SpinningTabbedPane) c).getSpinTabCount() + ")") : null;
}
private int getMovableTabCount()
{
int n = 0;
for (int i = 0, x = getTabCount(); i < x; ++i)
{
if (policy.canMove(i) && !isTabLockedAt(i))
{
++n;
}
}
return n;
}
@NotNull
private int[] getMovableTabIndices()
{
int x = getTabCount();
int[] list1 = new int[x];
int n = 0;
for (int i = 0; i < x; ++i)
{
if (policy.canMove(i) && !isTabLockedAt(i))
{
list1[n++] = i;
}
}
return Arrays.copyOf(list1, n);
}
private String getPlainTitleAt(int index)
{
String title = getTitleAt(index);
Component c = getComponentAt(index);
if (title == null)
{
return "";
}
if ((title.isEmpty()) || !(c instanceof SpinningTabbedPane))
{
return title;
}
String extra = getExtraTitleAt(index);
if (title.length() == extra.length())
{
return "";
}
// Stip extra and the one space separating it from the title.
return title.substring(0, title.length() - extra.length() - 1);
}
private int getSpinTabCount()
{
int n = getTabCount();
for (int i = 0, x = n; i < x; ++i)
{
Component c = getComponentAt(i);
if (!(c instanceof SpinningTabbedPane))
{
continue;
}
n -= 1; // don't count the spun tab itself
n += ((SpinningTabbedPane) c).getSpinTabCount();
}
return n;
}
private int getSpinTabPlacementAt(int index)
{
Component c = getComponentAt(index);
if (c instanceof SpinningTabbedPane)
{
return ((SpinningTabbedPane) c).getTabPlacement();
}
return -1;
}
private boolean isTabLockedAt(int index)
{
return locked.contains(getComponentAt(index));
}
/**
* Spin tabs starting at index to the end, stopping when we find
* another spun tab. If the first tab is itself spun, spin it as
* well; this permits spinning of spun tabs as a special case.
*
* @param index start spinning tabs here
* @param placement direction to place spun tabs
*/
private void spinTabsAt(int index, int placement)
{
SpinningTabbedPane pane = SpinningTabbedPane.createPane();
moveTabAtTo(index, -1, pane);
for (int i = index, x = getTabCount(); i < x; ++i)
{
Component c = getComponentAt(index);
if (c instanceof SpinningTabbedPane)
{
break;
}
moveTabAtTo(index, -1, pane);
}
add(pane, index);
setTitleAt(index, ""); // get count added
pane.parent = this;
pane.setTabPlacement(placement);
updateTabUIAt(index);
}
private void unlockTabAt(int index)
{
locked.remove(getComponentAt(index));
setIconAt(index, null);
}
/**
* Unspin all tabs in this pane.
*/
private void unspinAll()
{
int parentIndex = parent.indexOfComponent(this);
parent.removeTabAt(parentIndex);
for (int x = getTabCount(); --x >= 0;)
{
moveTabAtTo(x, parentIndex, parent);
}
parent = null; // help GC
}
private void unspinTabAt(int index)
{
if (getTabCount() == 1)
{
unspinAll();
}
else
{
int parentIndex = parent.indexOfComponent(this);
moveTabAtTo(index, parentIndex, parent);
parent.updateTabUIAt(parentIndex + 1);
}
}
private void moveTabAtTo(int fromIndex, int toIndex, @NotNull JTabbedPane to)
{
Component c = getComponentAt(fromIndex);
// Reparent incase we are unspinning a grandchild
if (c instanceof SpinningTabbedPane)
{
((SpinningTabbedPane) c).parent = this;
}
Color background = getBackgroundAt(fromIndex);
Icon disabledIcon = getDisabledIconAt(fromIndex);
Color foreground = getForegroundAt(fromIndex);
Icon icon = getIconAt(fromIndex);
String title = getTitleAt(fromIndex);
String tip = getToolTipTextAt(fromIndex);
removeTabAt(fromIndex);
if (toIndex == -1)
{
toIndex = to.getTabCount();
}
to.add(c, toIndex);
to.setBackgroundAt(toIndex, background);
to.setDisabledIconAt(toIndex, disabledIcon);
to.setForegroundAt(toIndex, foreground);
to.setIconAt(toIndex, icon);
to.setTitleAt(toIndex, title);
to.setToolTipTextAt(toIndex, tip);
// int displayedMnemonicIndex = getDisplayedMnemonicIndexAt(fromIndex);
// int mnemonic = getMnemonicAt(fromIndex);
// if (mnemonic != -1)
// to.setMnemonicAt(toIndex, mnemonic);
// if (displayedMnemonicIndex != -1)
// to.setDisplayedMnemonicIndexAt(toIndex, displayedMnemonicIndex);
}
private void updateTabUIAt(int index)
{
SpinningTabbedPane pane = (SpinningTabbedPane) getComponentAt(index);
int offset = SpinningTabbedPane.offsetForPlacement(pane.getTabPlacement()) + SpinningTabbedPane.TAB_OFFSET;
setTitleAt(index, getPlainTitleAt(index));
setIconAt(index, SpinningTabbedPane.icons[offset]);
setToolTipTextAt(index, SpinningTabbedPane.tips[offset]);
}
public interface PopupMenuPolicy
{
boolean canClose(int index);
boolean canGroup(int index);
boolean canLock(int index);
boolean canMove(int index);
boolean canNew(int index);
boolean canRename(int index);
boolean hasGroupMenu(int index, MouseEvent e);
boolean hasMoveMenu(int index, MouseEvent e);
boolean hasPlaceMenu(int index, MouseEvent e);
}
private final class DefaultPopupMenuPolicy implements PopupMenuPolicy
{
@Override
public boolean canClose(int index)
{
return true;
}
@Override
public boolean canGroup(int index)
{
return true;
}
@Override
public boolean canLock(int index)
{
return true;
}
@Override
public boolean canMove(int index)
{
return true;
}
@Override
public boolean canNew(int index)
{
return true;
}
@Override
public boolean canRename(int index)
{
return true;
}
@Override
public boolean hasGroupMenu(int index, MouseEvent e)
{
return true;
}
@Override
public boolean hasMoveMenu(int index, MouseEvent e)
{
return true;
}
@Override
public boolean hasPlaceMenu(int index, MouseEvent e)
{
return true;
}
}
private final class CloseAction extends IndexedAction
{
private CloseAction(int index)
{
super(index, LanguageBundle.getString("in_close"),
Utilities.CLOSE_ICON,
LanguageBundle.getMnemonic("in_mn_close"));
}
@Override
public void actionPerformed(ActionEvent e)
{
removeTabAt(getIndex());
}
}
private class GroupMenu extends JMenu
{
GroupMenu(int index, int placement)
{
super(LanguageBundle.getString("in_groupTabs"));
setMnemonic(LanguageBundle.getMnemonic("in_mn_groupTabs"));
Component c = SpinningTabbedPane.this.getComponentAt(index);
boolean first = true;
if (parent == null)
{ // we are not spun
if (c instanceof SpinningTabbedPane)
{ // tab is spun
add(new JMenuItem(new UngroupChildAction(index)));
addSeparator();
// Add backwards to get clockwise choices; skip
// tab's own direction since already spun
for (int j = 3; j > 0; --j)
{
add(new JMenuItem(new PlaceAction((SpinningTabbedPane) c,
SpinningTabbedPane.placementForSlot(j,
placement))));
}
first = false;
}
}
else
{ // we are spun
add(new JMenuItem(new UngroupSelfAction()));
if (c instanceof SpinningTabbedPane) // tab is spun
{
add(new JMenuItem(new UngroupChildAction(index)));
}
else // tab is not spun
{
add(new JMenuItem(new UngroupSingleAction(index)));
}
first = false;
}
if (policy.canGroup(index))
{
if (!first)
{
addSeparator();
}
// Add backwards to get clockwise choices
for (int j = 4; j > 0; --j)
{
add(new JMenuItem(new GroupAction(index,
SpinningTabbedPane.placementForSlot(j,
placement))));
}
}
}
}
private final class GroupAction extends IndexedAction
{
private final int placement;
private GroupAction(int index, int placement)
{
super(index, SpinningTabbedPane.offsetForPlacement(placement) + SpinningTabbedPane.GROUP_OFFSET);
this.placement = placement;
}
@Override
public void actionPerformed(ActionEvent e)
{
spinTabsAt(getIndex(), placement);
setSelectedIndex(getIndex());
}
}
private final class LockAction extends IndexedAction
{
private void lockTabAt(int index)
{
locked.add(getComponentAt(index));
setIconAt(index, Utilities.LOCK_ICON);
}
private LockAction(int index)
{
super(index, LanguageBundle.getString("in_lock"),
Utilities.LOCK_ICON, LanguageBundle.getMnemonic("in_mn_lock"));
}
@Override
public void actionPerformed(ActionEvent e)
{
lockTabAt(getIndex());
}
}
private class MoveActionListener implements ActionListener
{
int index;
int placement;
MoveActionListener(int index, int placement)
{
this.index = index;
this.placement = placement;
}
@Override
public void actionPerformed(ActionEvent e)
{
final int[] indices = getMovableTabIndices();
int i = -1;
switch (placement)
{
case SwingConstants.TOP:
i = indices[0];
break;
case SwingConstants.LEFT:
i = previous(index, indices);
break;
case SwingConstants.BOTTOM:
i = indices[indices.length - 1];
break;
case SwingConstants.RIGHT:
i = next(index, indices);
break;
}
moveTabAtTo(index, i, SpinningTabbedPane.this);
setSelectedIndex(i);
}
private int next(int current, @NotNull int[] indices)
{
for (int i = 0, x = indices.length - 1; i < x; ++i)
{
if (current == indices[i])
{
return indices[i + 1];
}
}
return -1;
}
private int previous(int current, @NotNull int[] indices)
{
for (int i = 1; i < indices.length; ++i)
{
if (current == indices[i])
{
return indices[i - 1];
}
}
return -1;
}
}
private class MoveMenu extends JMenu
{
MoveMenu(int index)
{
super(LanguageBundle.getString("in_moveTab")); //$NON-NLS-1$
setMnemonic(LanguageBundle.getMnemonic("in_mn_moveTab")); //$NON-NLS-1$
final int[] indices = getMovableTabIndices();
// Only you can prevent out of range errors.
int primum = -1;
int secundum = -1;
int penultimatum = -1;
int ultimatum = -1;
switch (indices.length)
{
case 0:
setEnabled(false);
break;
case 1:
setEnabled(false);
break;
case 2:
primum = indices[0];
secundum = Integer.MAX_VALUE;
penultimatum = Integer.MIN_VALUE;
ultimatum = indices[1];
break;
case 3:
primum = indices[0];
secundum = penultimatum = indices[1];
ultimatum = indices[2];
break;
default:
primum = indices[0];
secundum = indices[1];
penultimatum = indices[indices.length - 2];
ultimatum = indices[indices.length - 1];
}
for (int indice : indices)
{
if (index < indice)
{
continue;
}
if (index > primum)
{
if (index > secundum)
{
add(new MoveTabMenuItem(index, SwingConstants.TOP));
}
add(new MoveTabMenuItem(index, SwingConstants.LEFT));
}
if (index < ultimatum)
{
add(new MoveTabMenuItem(index, SwingConstants.RIGHT));
if (index < penultimatum)
{
add(new MoveTabMenuItem(index, SwingConstants.BOTTOM));
}
}
break;
}
}
}
private class MoveTabMenuItem extends JMenuItem
{
MoveTabMenuItem(int index, int placement)
{
int offset = SpinningTabbedPane.offsetForPlacement(placement);
switch (getTabPlacement())
{
case SwingConstants.TOP:
case SwingConstants.BOTTOM:
offset += SpinningTabbedPane.MOVE_LEFT_RIGHT_OFFSET;
break;
case SwingConstants.LEFT:
case SwingConstants.RIGHT:
offset += SpinningTabbedPane.MOVE_UP_DOWN_OFFSET;
break;
}
addActionListener(new MoveActionListener(index, placement));
SpinningTabbedPane.setMenuItem(this, offset);
}
}
private final class NewAction extends IndexedAction
{
private void addNewTab()
{
add(new JPanel());
}
private NewAction()
{
super(0, LanguageBundle.getString("in_new"), Utilities.NEW_ICON,
LanguageBundle.getMnemonic("in_mn_new"));
}
@Override
public void actionPerformed(ActionEvent e)
{
addNewTab();
}
}
private class PlaceMenu extends JMenu
{
PlaceMenu(int placement)
{
super(LanguageBundle.getString("in_placeTabs"));
setMnemonic(LanguageBundle.getMnemonic("in_mn_placeTabs"));
// Add backwards to get clockwise choices
for (int j = 3; j > 0; --j)
{
add(new JMenuItem(new PlaceAction(SpinningTabbedPane.this,
SpinningTabbedPane.placementForSlot(j,
placement))));
}
}
}
private final class PlaceAction extends IndexedAction
{
private final SpinningTabbedPane pane;
private final int placement;
private PlaceAction(SpinningTabbedPane pane, int placement)
{
super(0, SpinningTabbedPane.offsetForPlacement(placement) + SpinningTabbedPane.PLACE_OFFSET);
this.pane = pane;
this.placement = placement;
}
@Override
public void actionPerformed(ActionEvent e)
{
pane.setTabPlacement(placement);
}
}
private class PopupListener extends MouseAdapter
{
@Override
public void mousePressed(@NotNull MouseEvent e)
{
if (Utilities.isRightMouseButton(e))
{
final int x = e.getX();
final int y = e.getY();
final int index = indexAtLocation(x, y);
final int aTabPlacement = getTabPlacement();
JMenuItem newMenuItem = null;
if (policy.canNew(index))
{
newMenuItem = new JMenuItem(new NewAction());
}
JMenu moveMenu = null;
JMenu groupMenu = null;
JMenuItem renameMenuItem = null;
JMenuItem lockMenuItem = null;
JMenuItem closeMenuItem = null;
if (index >= 0)
{
final int spinTabPlacement = getSpinTabPlacementAt(index);
if (policy.canClose(index) && !isTabLockedAt(index))
{
closeMenuItem = new JMenuItem(new CloseAction(index));
}
if (policy.canLock(index))
{
lockMenuItem = isTabLockedAt(index) ? new JMenuItem(new UnlockAction(index))
: new JMenuItem(new LockAction(index));
}
if (policy.canRename(index))
{
renameMenuItem = new JMenuItem(new RenameAction(index, e));
}
if (policy.hasGroupMenu(index, e) && !isTabLockedAt(index))
{
groupMenu = new GroupMenu(index,
(spinTabPlacement == -1) ? aTabPlacement
: spinTabPlacement);
}
if (policy.hasMoveMenu(index, e) && (getMovableTabCount()
> 1) && !isTabLockedAt(index))
{
moveMenu = new MoveMenu(index);
}
}
JMenu placeMenu = null;
if (policy.hasPlaceMenu(index, e))
{
placeMenu = new PlaceMenu(aTabPlacement);
}
final boolean useNewMenuItem = newMenuItem != null;
final boolean useCloseMenuItem = closeMenuItem != null;
final boolean useLockMenuItem = lockMenuItem != null;
final boolean useRenameMenuItem = renameMenuItem != null;
boolean useGroupMenu = (groupMenu != null) &&
(groupMenu.getMenuComponentCount() > 0);
final boolean useMoveMenu = (moveMenu != null) &&
(moveMenu.getMenuComponentCount() > 0);
final boolean usePlaceMenu = (placeMenu != null) &&
(placeMenu.getMenuComponentCount() > 0);
JPopupMenu popupMenu = new JPopupMenu();
if ((popupMenu.getComponentCount() > 0) && (useNewMenuItem ||
useCloseMenuItem))
{
popupMenu.addSeparator();
}
if (useNewMenuItem)
{
popupMenu.add(newMenuItem);
}
if (useCloseMenuItem)
{
popupMenu.add(closeMenuItem);
}
if ((popupMenu.getComponentCount() > 0) && (useLockMenuItem ||
useRenameMenuItem))
{
popupMenu.addSeparator();
}
if (useLockMenuItem)
{
popupMenu.add(lockMenuItem);
}
if (useRenameMenuItem)
{
popupMenu.add(renameMenuItem);
}
if ((popupMenu.getComponentCount() > 0) && (useGroupMenu ||
useMoveMenu || usePlaceMenu))
{
popupMenu.addSeparator();
}
if (useGroupMenu)
{
popupMenu.add(groupMenu);
}
if (useMoveMenu)
{
popupMenu.add(moveMenu);
}
if (usePlaceMenu)
{
popupMenu.add(placeMenu);
}
//Commented out as the method contains no code.
//addPopupMenuItems(popupMenu, index, e);
popupMenu.show(e.getComponent(), x, y);
}
// As a shortcut, spin clockwise.
else if (Utilities.isShiftLeftMouseButton(e))
{
final int index = indexAtLocation(e.getX(), e.getY());
// 3 is magic; it's the next clock position. XXX
spinTabsAt(index, SpinningTabbedPane.placementForSlot(3, getTabPlacement()));
setSelectedIndex(index);
}
}
}
private final class RenameAction extends IndexedAction
{
private final MouseEvent evt;
private RenameAction(int index, MouseEvent e)
{
super(index, LanguageBundle.getString("in_rename") + "...", null,
LanguageBundle.getMnemonic("in_mn_rename"));
this.evt = e;
}
@Override
public void actionPerformed(ActionEvent e)
{
int x = evt.getX();
int y = evt.getY();
String title = getPlainTitleAt(getIndex());
JTextField textField = new JTextField(title);
Logging.errorPrint("document? " + textField.getDocument());
JPopupMenu popupMenu = new JPopupMenu();
textField.addActionListener(new RenameTextFieldActionListener(getIndex(),
textField,
popupMenu));
popupMenu.add(textField);
Component c = evt.getComponent();
// Because this doesn't have a width/height before being
// shown, need to show it them move it.
popupMenu.show(c, x, y);
// These don't seem to work. ?? XXX
textField.selectAll();
textField.setCaretPosition(title.length());
// Workaround bug in JDK1.4 (and earlier?): if JTextField
// slops past edge of the pane window, you can't stick the
// cursor in it. XXX
Component pane = getComponentAt(getIndex());
Point paneLocation = pane.getLocationOnScreen();
Point popupLocation = popupMenu.getLocationOnScreen();
Dimension paneSize = pane.getSize();
Dimension popupSize = popupMenu.getSize();
boolean reshow = false;
if ((popupLocation.x + popupSize.width) >= (paneLocation.x +
paneSize.width))
{
reshow = true;
x = (paneLocation.x + paneSize.width) - popupSize.width - 1;
}
if ((popupLocation.y + popupSize.height) >= (paneLocation.y +
paneSize.height))
{
reshow = true;
y = (paneLocation.y + paneSize.height) - popupSize.height - 1;
}
if (reshow)
{
popupMenu.show(c, x, y);
}
}
}
private class RenameTextFieldActionListener implements ActionListener
{
private final JPopupMenu popupMenu;
private final JTextField textField;
private final int anIndex;
RenameTextFieldActionListener(int index, JTextField textField,
JPopupMenu popupMenu)
{
this.anIndex = index;
this.textField = textField;
this.popupMenu = popupMenu;
}
@Override
public void actionPerformed(ActionEvent e)
{
setTitleAt(anIndex, textField.getText());
popupMenu.setVisible(false); // why? XXX
}
}
private final class UngroupChildAction extends IndexedAction
{
private UngroupChildAction(int index)
{
super(index, SpinningTabbedPane.offsetForPlacement(getTabPlacement()) +
SpinningTabbedPane.UNGROUP_CHILD_OFFSET);
}
@Override
public void actionPerformed(ActionEvent e)
{
SpinningTabbedPane pane = (SpinningTabbedPane) getComponentAt(getIndex());
final int newIndex = pane.getSelectedIndex();
pane.unspinAll();
setSelectedIndex(getIndex() + newIndex);
}
}
private final class UngroupSelfAction extends IndexedAction
{
private UngroupSelfAction()
{
super(0, SpinningTabbedPane.offsetForPlacement(parent.getTabPlacement()) +
SpinningTabbedPane.UNGROUP_SELF_OFFSET);
}
@Override
public void actionPerformed(ActionEvent e)
{
final int index = parent.indexOfComponent(SpinningTabbedPane.this);
final int newIndex = getSelectedIndex();
SpinningTabbedPane aParent = parent;
unspinAll();
aParent.setSelectedIndex(index + newIndex);
}
}
private final class UngroupSingleAction extends IndexedAction
{
private UngroupSingleAction(int index)
{
super(index, SpinningTabbedPane.offsetForPlacement(parent.getTabPlacement()) +
SpinningTabbedPane.UNGROUP_SINGLE_OFFSET);
}
@Override
public void actionPerformed(ActionEvent e)
{
unspinTabAt(getIndex());
parent.setSelectedIndex(parent.indexOfComponent(SpinningTabbedPane.this) -
1);
}
}
private final class UnlockAction extends IndexedAction
{
private UnlockAction(int index)
{
super(index, LanguageBundle.getString("in_unlock"),
Utilities.LOCK_ICON,
LanguageBundle.getMnemonic("in_mn_unlock"));
}
@Override
public void actionPerformed(ActionEvent e)
{
unlockTabAt(getIndex());
}
}
private abstract static class IndexedAction extends AbstractAction
{
private final int index;
private IndexedAction(int index, int offset)
{
this(index, SpinningTabbedPane.labels[offset], SpinningTabbedPane.icons[offset]);
String label = SpinningTabbedPane.labels[offset];
if (label != null)
{
putValue(Action.MNEMONIC_KEY, (int) label.charAt(0));
}
putValue(Action.SHORT_DESCRIPTION, SpinningTabbedPane.tips[index]);
}
private IndexedAction(int index, String name, ImageIcon icon)
{
super(name, icon);
this.index = index;
}
private IndexedAction(int index, String name, ImageIcon icon,
int mnemonic)
{
this(index, name, icon);
putValue(Action.MNEMONIC_KEY, mnemonic);
}
public int getIndex()
{
return index;
}
}
}