/*
* Copyright 2003-2012 Yusuke Yamamoto
*
* 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 samurai.swing;
import samurai.util.GUIResourceBundle;
import samurai.util.OSDetector;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
public class TileTabPanel<T extends JComponent> extends JPanel implements MouseListener, MouseMotionListener {
private static GUIResourceBundle resources = GUIResourceBundle.getInstance();
JPopupMenu popupMenu = new JPopupMenu();
private JMenuItem menuCloseTab = new JMenuItem("TileTabPanel.closeTab");
private JMenuItem menuTab = new JMenuItem("TileTabPanel.tab");
private JMenuItem menuHorizontal = new JMenuItem("TileTabPanel.splitHorizontal");
private JMenuItem menuVertical = new JMenuItem("TileTabPanel.splitVertical");
JMenuItem jMenuViewTab = new JMenuItem("TileTabPanel.tab");
JMenuItem jMenuViewSplitHorizontal = new JMenuItem("TileTabPanel.splitHorizontal");
JMenuItem jMenuViewSplitVertical = new JMenuItem("TileTabPanel.splitVertical");
private static ImageIcon closeIcon;
private static ImageIcon closePushedIcon;
private static ImageIcon closeHoverIcon;
private static ImageIcon nullIcon = new ImageIcon(TileTabPanel.class.getResource(
"null.gif"));
static {
if (OSDetector.isMac()) {
closeIcon = new ImageIcon(TileTabPanel.class.getResource(
"close.gif"));
closeHoverIcon = closeIcon;
// closeHoverIcon = new ImageIcon(TileTabPanel.class.getResource(
// "close_hover.gif"));
closePushedIcon = new ImageIcon(TileTabPanel.class.getResource(
"close_push.gif"));
} else {
closeIcon = new ImageIcon(TileTabPanel.class.getResource(
"winclose.gif"));
closeHoverIcon = new ImageIcon(TileTabPanel.class.getResource(
"winclose_hover.gif"));
closePushedIcon = new ImageIcon(TileTabPanel.class.getResource(
"winclose_push.gif"));
}
}
private boolean closable = false;
private CloseListener closeListener = null;
public void enableClosable(CloseListener listener) {
if (!closable) {
popupMenu.insert(new JSeparator(), 0);
popupMenu.insert(menuCloseTab, 0);
}
closeListener = listener;
this.closable = true;
}
public void disableClosable() {
if (closable) {
popupMenu.remove(0);
popupMenu.remove(0);
}
closeListener = null;
this.closable = false;
}
public boolean isClosable() {
return this.closable;
}
private List<ComponentInfo<T>> components = new ArrayList<ComponentInfo<T>>(3);
private TilePanel tilePanel;
JTabbedPane tab = new JTabbedPane();
BorderLayout borderLayout1;
private int layout = TAB;
public static final int TAB = 0;
public static final int TILE_HORIZONTAL = 1;
public static final int TILE_VERTICAL = 2;
private boolean showTitleWithSingleComponent = true;
public void setShowTitleWithSingleComponent(boolean show) {
this.showTitleWithSingleComponent = show;
}
public TileTabPanel() {
this(false);
}
public TileTabPanel(boolean supportsFocusable) {
tilePanel = new TilePanel(supportsFocusable);
try {
jbInit();
} catch (Exception exception) {
exception.printStackTrace();
}
}
public void setForegroundAt(int index, Color color) {
if (layout == TAB) {
tab.setForegroundAt(index, color);
} else {
tilePanel.setForegroundAt(index, color);
}
}
public Color getForegroundAt(int index) {
if (layout == TAB) {
return tab.getForegroundAt(index);
} else {
return tilePanel.getForegroundAt(index);
}
}
private CloseAction closeAction = new CloseAction(-1);
class CloseAction implements ActionListener {
private int index;
CloseAction(int index) {
this.index = index;
}
public void setIndex(int index) {
this.index = index;
}
public void actionPerformed(ActionEvent e) {
closeListener.closePushed(index);
}
}
;
class OrientationChangeAction implements ActionListener {
private int orientation;
OrientationChangeAction(int orientation) {
this.orientation = orientation;
}
public void actionPerformed(ActionEvent e) {
setOrientation(orientation);
}
}
private void jbInit() throws Exception {
setDoubleBuffered(true);
borderLayout1 = new BorderLayout();
setLayout(borderLayout1);
tab.addMouseListener(this);
tab.addMouseMotionListener(this);
tilePanel.addMouseListnerToTitles(this);
tilePanel.addMouseMotionListnerToTitles(this);
menuCloseTab.addActionListener(closeAction);
menuTab.setEnabled(false);
menuTab.addActionListener(new OrientationChangeAction(TAB));
menuHorizontal.addActionListener(new OrientationChangeAction(TILE_HORIZONTAL));
menuVertical.addActionListener(new OrientationChangeAction(TILE_VERTICAL));
popupMenu.add(menuTab);
popupMenu.add(menuHorizontal);
popupMenu.add(menuVertical);
jMenuViewTab.addActionListener(new OrientationChangeAction(TAB));
jMenuViewTab.setEnabled(false);
jMenuViewSplitHorizontal.addActionListener(new OrientationChangeAction(TILE_HORIZONTAL));
jMenuViewSplitVertical.addActionListener(new OrientationChangeAction(TILE_VERTICAL));
// keyStroke.apply(popupMenu);
}
public int getComponentSize() {
return components.size();
}
public void addComponent(String name, T component) {
addComponent(name, component, null);
}
public void addComponent(String name, T component, ImageIcon icon) {
ComponentInfo<T> componentInfo = new ComponentInfo<T>(component, name, icon);
if (null == icon && closable) {
componentInfo.setIcon(nullIcon);
}
this.components.add(componentInfo);
switch (this.components.size()) {
case 1:
if (!this.showTitleWithSingleComponent) {
add((Component) this.components.get(0).getComponent(), BorderLayout.CENTER);
validate();
} else {
if (this.layout == TAB) {
add(tab, BorderLayout.CENTER);
validate();
} else {
add(tilePanel, BorderLayout.CENTER);
validate();
}
}
break;
case 2:
if (!this.showTitleWithSingleComponent) {
remove(components.get(0).getComponent());
if (this.layout == TAB) {
remove(components.get(0).getComponent());
add(tab, BorderLayout.CENTER);
tab.addTab(components.get(0).getName(), components.get(0).getIcon(),
components.get(0).getComponent());
validate();
} else {
remove(components.get(0).getComponent());
add(tilePanel, BorderLayout.CENTER);
validate();
tilePanel.addComponent(components.get(0).getName(), components.get(0).getIcon(),
components.get(0).getComponent());
}
}
break;
default:
}
if (showTitleWithSingleComponent || 1 < components.size()) {
if (this.layout == TAB) {
tab.add(name, component);
tab.setIconAt(tab.getTabCount() - 1, componentInfo.getIcon());
this.currentSelectedIndex = tab.getSelectedIndex();
} else {
tilePanel.addComponent(name, component);
tilePanel.setLeftIconAt(tilePanel.getTileCount() - 1, componentInfo.getIcon());
this.currentSelectedIndex = tilePanel.getSelectedIndex();
}
}
}
public void removeComponent(T component) {
for (int i = 0; i < components.size(); i++) {
if (components.get(i).getComponent() == component) {
removeComponentAt(i);
break;
}
}
}
public void removeComponentAt(int index) {
T component = getComponentAt(index);
if (component instanceof RemoveListener) {
((RemoveListener) component).willBeRemoved();
}
components.remove(index);
if (this.layout == TAB) {
tab.remove(component);
this.currentSelectedIndex = tab.getSelectedIndex();
} else {
this.tilePanel.removeComponent(component);
this.currentSelectedIndex = tilePanel.getSelectedIndex();
}
if (!this.showTitleWithSingleComponent && components.size() == 1) {
if (this.layout == TAB) {
tab.removeTabAt(0);
this.remove(tab);
} else {
tilePanel.removeTileAt(0);
this.remove(tilePanel);
}
this.add(components.get(0).getComponent(), BorderLayout.CENTER);
}
if (components.size() == 0) {
if (!this.showTitleWithSingleComponent) {
this.remove(component);
} else if (layout == TAB) {
this.remove(tab);
} else {
this.remove(tilePanel);
}
}
validate();
repaint();
}
public void setIconAt(int index, ImageIcon icon) {
components.get(index).setIcon(icon);
if (showTitleWithSingleComponent || 1 < components.size()) {
if (this.layout == TAB) {
this.tab.setIconAt(index, icon);
} else {
this.tilePanel.setLeftIconAt(index, icon);
}
}
}
public void setTitleAt(int index, String text) {
components.get(index).setName(text);
if (showTitleWithSingleComponent || 1 < components.size()) {
if (this.layout == TAB) {
this.tab.setTitleAt(index, text);
} else {
this.tilePanel.setTitleAt(index, text);
}
}
}
public void setComponentAt(int index, T component) {
if (showTitleWithSingleComponent || 1 < components.size()) {
if (this.layout == TAB) {
this.tab.setComponentAt(index, component);
} else {
this.tilePanel.setComponentAt(index, component);
}
} else {
this.remove(components.get(index).getComponent());
this.add(component, BorderLayout.CENTER);
}
components.get(index).setComponent(component);
}
public void setSelectedIndex(int index) {
this.currentSelectedIndex = index;
if (showTitleWithSingleComponent || 1 < components.size()) {
if (TAB == layout) {
tab.setSelectedIndex(index);
} else {
tilePanel.setSelectedIndex(index);
}
selectedIndexChanged(index);
} else {
// do nothing
}
}
protected void selectedIndexChanged(int index){
}
public int getSelectedIndex() {
if (showTitleWithSingleComponent || 1 < components.size()) {
if (TAB == layout) {
return tab.getSelectedIndex();
} else {
return tilePanel.getSelectedIndex();
}
} else {
return 0;
}
}
public T getSelectedComponent() {
if (showTitleWithSingleComponent || 1 < components.size()) {
if (TAB == layout) {
return (T) tab.getSelectedComponent();
} else {
return (T) tilePanel.getSelectedComponent();
}
} else {
return components.get(0).getComponent();
}
}
public T getComponentAt(int index) {
if (showTitleWithSingleComponent || 1 < components.size()) {
if (TAB == layout) {
return (T) tab.getComponentAt(index);
} else {
return (T) tilePanel.getComponentAt(index);
}
} else {
return components.get(index).getComponent();
}
}
private int currentSelectedIndex = 0;
public ImageIcon getIconAt(int index) {
return components.get(index).getIcon();
}
public void setOrientation(int orientation) {
if (orientation == TILE_HORIZONTAL) {
tilePanel.setOrientation(TilePanel.HORIZONTAL);
} else {
tilePanel.setOrientation(TilePanel.VERTICAL);
}
this.currentSelectedIndex = getSelectedIndex();
if (this.layout != orientation) {
switch (this.components.size()) {
case 0:
break;
case 1:
if (!this.showTitleWithSingleComponent) {
break;
}
default:
if (TAB == orientation) {
remove(tilePanel);
add(tab, BorderLayout.CENTER);
tab.removeAll();
for (int i = 0; i < components.size(); i++) {
tab.addTab(components.get(i).getName(), components.get(i).getIcon(),
components.get(i).getComponent());
}
tab.setSelectedIndex(this.currentSelectedIndex);
validate();
} else {
//layout is tile
if (TAB == layout) {
// layout used to be TAB
remove(tab);
add(tilePanel, BorderLayout.CENTER);
tilePanel.removeAll();
validate();
for (int i = 0; i < components.size(); i++) {
tilePanel.addComponent(components.get(i).getName(), components.get(i).getIcon(),
components.get(i).getComponent());
}
tilePanel.setSelectedIndex(this.currentSelectedIndex);
}
// if (orientation == TILE_HORIZONTAL) {
// tilePanel.setOrientation(TilePanel.HORIZONTAL);
// } else {
// tilePanel.setOrientation(TilePanel.VERTICAL);
// }
validate();
}
}
this.layout = orientation;
setMenuAvailability();
}
}
private void setMenuAvailability() {
jMenuViewTab.setEnabled(layout != TAB);
menuTab.setEnabled(layout != TAB);
jMenuViewSplitVertical.setEnabled(layout != TILE_VERTICAL);
menuVertical.setEnabled(layout != TILE_VERTICAL);
jMenuViewSplitHorizontal.setEnabled(layout != TILE_HORIZONTAL);
menuHorizontal.setEnabled(layout != TILE_HORIZONTAL);
}
public void setDividerSize(int size){
this.tilePanel.setDeviderSize(size);
}
public int indexAtLocation(int x, int y) {
if (layout == TAB) {
return tab.indexAtLocation(x, y);
} else {
return -1;
}
}
private int indexAtLocation(MouseEvent event) {
if (layout == TAB) {
return tab.indexAtLocation(event.getX(), event.getY());
} else {
return tilePanel.indexAtLocation(event);
}
}
public String getTitleAt(int index) {
return components.get(index).getName();
}
public void mouseClicked(MouseEvent event) {
handlePopup(event);
}
private void handlePopup(MouseEvent event) {
if (event.isPopupTrigger()) {
event.consume();
int clickedTabIndex = this.indexAtLocation(event);
closeAction.setIndex(clickedTabIndex);
menuCloseTab.setText(resources.getMessage("TileTabPanel.closeTab", this.getTitleAt(clickedTabIndex)));
popupMenu.show(event.getComponent(), event.getX(), event.getY());
}
}
private int draggingPaneIndex = -1;
/**
* method for MouseMotionListener
*
* @param event MouseEvent
*/
boolean pressedOnButton = false;
int pressedIndex = -1;
public void mousePressed(MouseEvent event) {
this.draggingPaneIndex = indexAtLocation(event);
if (isClosable()) {
if (event.getButton() == MouseEvent.BUTTON1) {
pressedOnButton = isOnButton(draggingPaneIndex, event);
pressedIndex = indexAtLocation(event);
if (-1 != pressedIndex) {
if (pressedOnButton) {
event.consume();
if (layout == TAB) {
tab.setIconAt(draggingPaneIndex, isOnButton(draggingPaneIndex, event) ? closePushedIcon : closeIcon);
} else {
if (OSDetector.isMac()) {
this.tilePanel.setLeftIconAt(draggingPaneIndex, isOnButton(draggingPaneIndex, event) ? closePushedIcon : closeIcon);
} else {
this.tilePanel.setRightIconAt(draggingPaneIndex, isOnButton(draggingPaneIndex, event) ? closePushedIcon : closeIcon);
}
}
}
}
}
}
if (!event.isConsumed()) {
handlePopup(event);
}
}
public void mouseDragged(MouseEvent event) {
if (draggingPaneIndex == -1) {
return;
} else {
if (isClosable()) {
if (0 != (event.getButton() & MouseEvent.BUTTON1)) {
if (-1 == indexAtLocation(event)) {
restoreIcon();
} else if (layout == TAB) {
tab.setIconAt(draggingPaneIndex, pressedOnButton & isOnButton(pressedIndex, event) ? closePushedIcon : closeIcon);
} else {
if (OSDetector.isMac()) {
tilePanel.setLeftIconAt(draggingPaneIndex, pressedOnButton & isOnButton(pressedIndex, event) ? closePushedIcon : closeIcon);
} else {
tilePanel.setRightIconAt(draggingPaneIndex, pressedOnButton & isOnButton(pressedIndex, event) ? closePushedIcon : closeIcon);
}
}
}
}
}
int targetTabIndex = indexAtLocation(event);
if (!pressedOnButton) {
movePane(targetTabIndex);
}
}
/**
* method for MouseMotionListener
*
* @param event MouseEvent
*/
public void mouseReleased(MouseEvent event) {
if (isClosable()) {
if ((0 != (event.getButton() & MouseEvent.BUTTON1)) && draggingPaneIndex == indexAtLocation(event) && isOnButton(pressedIndex, event) && pressedOnButton) {
restoreIcon();
closeListener.closePushed(indexAtLocation(event));
event.consume();
} else {
if (-1 == indexAtLocation(event)) {
restoreIcon();
} else {
setCloseIcon(event);
}
}
}
if (!event.isConsumed()) {
handlePopup(event);
}
draggingPaneIndex = -1;
pressedIndex = -1;
pressedOnButton = false;
}
public void mouseEntered(MouseEvent event) {
if (isClosable()) {
setCloseIcon(event);
}
}
public void mouseExited(MouseEvent mouseevent) {
if (isClosable()) {
restoreIcon();
}
}
/**
* method for MouseMotionListener
*
* @param event MouseEvent
*/
public void mouseMoved(MouseEvent event) {
if (isClosable()) {
setCloseIcon(event);
}
}
private boolean isOnButton(int index, MouseEvent event) {
int delta;
int currentIndex = indexAtLocation(event);
if (-1 == currentIndex || currentIndex != index) {
return false;
}
if (layout == TAB) {
delta = event.getX() - (int) tab.getBoundsAt(currentIndex).getX() - 12;
return 0 <= delta && delta < 18;
} else {
return tilePanel.isOnButton(index, event);
}
}
int lastEnteredIndex = -1;
private void setCloseIcon(MouseEvent event) {
int index = indexAtLocation(event);
if (index != lastEnteredIndex && -1 != lastEnteredIndex) {
restoreIcon();
} else {
if (index >= 0) {
if (layout == TAB) {
tab.setIconAt(index, isOnButton(index, event) ? closeHoverIcon : closeIcon);
} else {
if (OSDetector.isMac()) {
tilePanel.setLeftIconAt(index, isOnButton(index, event) ? closeHoverIcon : closeIcon);
} else {
tilePanel.setRightIconAt(index, isOnButton(index, event) ? closeHoverIcon : closeIcon);
}
}
}
}
lastEnteredIndex = index;
}
private void restoreIcon() {
if (lastEnteredIndex >= 0 && lastEnteredIndex < components.size()) {
if (layout == TAB) {
tab.setIconAt(lastEnteredIndex, getIconAt(lastEnteredIndex));
} else {
if (OSDetector.isMac()) {
tilePanel.setLeftIconAt(lastEnteredIndex, getIconAt(lastEnteredIndex));
} else {
tilePanel.setRightIconAt(lastEnteredIndex, nullIcon);
}
}
}
}
private void movePane(int destination) {
if (destination != -1 && destination != draggingPaneIndex) {
boolean isForwardDrag = destination > draggingPaneIndex;
tab.insertTab(tab.getTitleAt(draggingPaneIndex),
tab.getIconAt(draggingPaneIndex),
tab.getComponentAt(draggingPaneIndex),
tab.getToolTipTextAt(draggingPaneIndex),
isForwardDrag ? destination + 1 : destination);
draggingPaneIndex = destination;
tab.setSelectedIndex(draggingPaneIndex);
ComponentInfo ci1 = components.get(draggingPaneIndex);
ComponentInfo ci2 = components.get(destination);
components.set(draggingPaneIndex, ci2);
components.set(destination, ci1);
}
}
}
class ComponentInfo<T2 extends Component> {
private T2 component;
private String name;
private ImageIcon icon;
private double width;
private double height;
ComponentInfo(T2 component, String name, ImageIcon icon) {
this.component = component;
this.name = name;
this.icon = icon;
}
public T2 getComponent() {
return this.component;
}
public void setIcon(ImageIcon icon) {
this.icon = icon;
}
public ImageIcon getIcon() {
return this.icon;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setComponent(T2 component) {
this.component = component;
}
public void setWidth(double size) {
this.width = size;
}
public void setHeight(double size) {
this.height = size;
}
public double getWidth() {
return this.width;
}
public double getHeight() {
return this.height;
}
}