/*
GanttProject is an opensource project management tool. License: GPL3
Copyright (C) 2010 Dmitry Barashev
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.sourceforge.ganttproject.chart.overview;
import biz.ganttproject.core.chart.render.TextLengthCalculatorImpl;
import biz.ganttproject.core.option.ChangeValueEvent;
import biz.ganttproject.core.option.ChangeValueListener;
import biz.ganttproject.core.option.GPOption;
import biz.ganttproject.core.option.IntegerOption;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import net.sourceforge.ganttproject.gui.ActionUtil;
import net.sourceforge.ganttproject.gui.TestGanttRolloverButton;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.gui.UIUtil;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.MessageFormat;
/**
* This class is a standard way of building toolbars in GanttProject.
*
* @author Dmitry Barashev (dbarashev@ganttproject.biz)
*/
public class ToolbarBuilder {
public static class Gaps {
public static Supplier<Component> VDASH = new Supplier<Component>() {
@Override
public Component get() {
return new JLabel(" | ");
}
};
public static Supplier<Component> RIGID = new Supplier<Component>() {
@Override
public Component get() {
return Box.createRigidArea(new Dimension(3, 0));
}
};
}
private final JPanel myToolbar;
private Border myBorder = BorderFactory.createEmptyBorder(2,2,2,2);
private Color myBackground;
private IntegerOption myDpiOption;
private GPOption<String> myLafOption;
private final java.util.List<TestGanttRolloverButton> myButtons = Lists.newArrayList();
private Supplier<Component> myGapFactory;
private int myBaseHeight;
private boolean myButtonsSquared = false;
private final java.util.List<ChangeValueListener> myOnUiChangeListeners = Lists.newArrayList();
public ToolbarBuilder() {
myToolbar = new JPanel() {
@Override
public Dimension getPreferredSize() {
// This is a hack which keeps the preferred height of the whole
// toolbar equal to the preferred height of its first button.
// It may help when one of the components initially is taller than other
// components and thus adds small padding between the box and button borders.
Dimension d = super.getPreferredSize();
Dimension first = getComponent(0).getPreferredSize();
d.height = first.height;
return d;
}
};
myToolbar.setLayout(new BoxLayout(myToolbar, BoxLayout.LINE_AXIS));
myToolbar.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
myBackground = myToolbar.getBackground();
}
public ToolbarBuilder withBackground(Color background) {
myBackground = background;
return this;
}
public ToolbarBuilder withBorder(Border border) {
myBorder = border;
return this;
}
public ToolbarBuilder withSquareButtons() {
myButtonsSquared = true;
return this;
}
public ToolbarBuilder withDpiOption(IntegerOption dpiOption) {
myDpiOption = dpiOption;
return this;
}
public ToolbarBuilder withGapFactory(Supplier<Component> gapFactory) {
myGapFactory = Preconditions.checkNotNull(gapFactory);
return this;
}
public ToolbarBuilder withHeight(int baseHeight) {
myBaseHeight = baseHeight;
return this;
}
public ToolbarBuilder withLafOption(GPOption<String> lafOption, Function<String, Float> buttonSizeScaling) {
myLafOption = lafOption;
return this;
}
public class ButtonBuilder {
private final TestGanttRolloverButton myButton;
ButtonBuilder(TestGanttRolloverButton button) {
myButton = Preconditions.checkNotNull(button);
button.setIcon(null);
button.setRolloverIcon(null);
button.setHorizontalTextPosition(SwingConstants.CENTER);
button.setVerticalTextPosition(SwingConstants.CENTER);
button.setTextHidden(false);
button.setAlignmentY(Component.CENTER_ALIGNMENT);
button.setMargin(new Insets(0, 0, 0, 0));
}
public ButtonBuilder withAutoRepeat(int millis) {
ActionUtil.setupAutoRepeat(myButton, millis);
return this;
}
public ToolbarBuilder add() {
myButtons.add(myButton);
addGap();
myToolbar.add(myButton);
return ToolbarBuilder.this;
}
}
public ToolbarBuilder addButton(TestGanttRolloverButton button) {
return new ButtonBuilder(button).add();
}
public ToolbarBuilder addButton(Action action) {
return addButton(new TestGanttRolloverButton(action));
}
public ButtonBuilder button(Action action) {
return new ButtonBuilder(new TestGanttRolloverButton(action));
}
public ToolbarBuilder addPanel(JPanel panel) {
addGap();
panel.setAlignmentY(Component.CENTER_ALIGNMENT);
myToolbar.add(panel);
return this;
}
private void addGap() {
if (myToolbar.getComponentCount() != 0 && myGapFactory != null) {
myToolbar.add(myGapFactory.get());
}
}
public ToolbarBuilder addWhitespace() {
float scale = myDpiOption == null ? 1.0f : myDpiOption.getValue().floatValue() / UIFacade.DEFAULT_DPI;
int whitespaceWidth = (int)(myBaseHeight * scale / 1.62f);
myToolbar.add(Box.createRigidArea(new Dimension(whitespaceWidth, 0)));
return this;
}
public ToolbarBuilder addComboBox(final Action[] actions, final Action selected) {
class MyComboBox extends TestGanttRolloverButton {
private Action mySelectedAction = null;
private final Action[] myActions;
private Rectangle myIconRect;
private Dimension myPreferredSize;
private MyComboBox(Action[] actions) {
myActions = actions;
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
onMouseClicked(e);
}
});
setIcon(new ImageIcon(getClass().getResource("/icons/dropdown_16.png")) {
@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
super.paintIcon(c, g, x, y);
myIconRect = new Rectangle(x, y, 16, 16);
}
});
setHorizontalTextPosition(LEADING);
setVerticalTextPosition(SwingConstants.CENTER);
int maxLength = 0;
for (Action a : actions) {
if (getActionName(a).length() > maxLength) {
maxLength = getActionName(a).length();
}
}
setSelectedAction(selected);
setHorizontalAlignment(SwingConstants.RIGHT);
}
private void setSelectedAction(Action selected) {
mySelectedAction = selected;
getButton().setText(formatActionName(selected));
resetPreferredSize();
}
public void setText(String arg0) {
super.setText(arg0);
FontMetrics metrics = getFontMetrics(getFont());
int width = metrics.stringWidth( getText() );
int height = metrics.getHeight();
Dimension newDimension = getPreferredSize();
newDimension.setSize(width+16, newDimension.height);
setPreferredSize(newDimension);
setBounds(new Rectangle(
getLocation(), getPreferredSize()));
}
private String getActionName(Action a) {
return a.getValue(Action.NAME).toString();
}
private String formatActionName(Action a) {
String name = getActionName(a);
name = name.replace(" ", " ");
return MessageFormat.format("<html><b>{0}</b></html>", name);
}
protected void onMouseClicked(MouseEvent e) {
if (myIconRect.contains(e.getX(), e.getY())) {
showPopup();
} else {
mySelectedAction.actionPerformed(null);
}
}
private void showPopup() {
JPopupMenu popupMenu = new JPopupMenu();
for (final Action a : myActions) {
popupMenu.add(new AbstractAction(a.getValue(Action.NAME).toString()) {
@Override
public void actionPerformed(ActionEvent e) {
a.actionPerformed(e);
setSelectedAction(a);
}
});
}
popupMenu.show(myToolbar, getButton().getLocation().x, getButton().getHeight());
}
private JButton getButton() {
return MyComboBox.this;
}
@Override
public Dimension getPreferredSize() {
if (myPreferredSize != null) {
return myPreferredSize;
}
Dimension d = super.getPreferredSize();
Graphics2D g = (Graphics2D) getGraphics();
if (g == null) {
return d;
}
int maxLength = 0;
int maxHeight = 0;
TextLengthCalculatorImpl textLength = new TextLengthCalculatorImpl(g);
for (Action a : myActions) {
String text = a.getValue(Action.NAME).toString();
maxLength = Math.max(maxLength, textLength.getTextLength(text));
maxHeight = Math.max(maxHeight, textLength.getTextHeight(text));
}
int width = (int) (maxLength * 1.1) + 16 + getIconTextGap();
Insets insets = getInsets();
myPreferredSize = new Dimension(width + insets.left + insets.right, maxHeight);
return myPreferredSize;
}
void resetPreferredSize() {
myPreferredSize = null;
}
}
final MyComboBox button = new MyComboBox(actions);
addGap();
myToolbar.add(button);
myOnUiChangeListeners.add(new ChangeValueListener() {
@Override
public void changeValue(ChangeValueEvent event) {
button.resetPreferredSize();
}
});
return this;
}
public GPToolbar build() {
UIUtil.setBackgroundTree(myToolbar, myBackground);
final GPToolbar result = new GPToolbar(myToolbar, myButtons, myBaseHeight, myButtonsSquared, myDpiOption);
if (myLafOption != null) {
ChangeValueListener lafListener = new ChangeValueListener() {
@Override
public void changeValue(ChangeValueEvent event) {
for (ChangeValueListener l : myOnUiChangeListeners) {
l.changeValue(event);
}
}
};
myLafOption.addChangeValueListener(lafListener);
lafListener.changeValue(null);
if (myDpiOption != null) {
myDpiOption.addChangeValueListener(lafListener);
}
}
result.getToolbar().setBorder(myBorder);
result.updateButtons();
return result;
}
}