/*
Copyright 2003-2012 Dmitry Barashev, GanttProject Team
This file is part of GanttProject, an opensource project management tool.
GanttProject 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.
GanttProject 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 GanttProject. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.ganttproject.gui;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLEditorKit;
import net.sourceforge.ganttproject.action.GPAction;
import net.sourceforge.ganttproject.language.GanttLanguage;
public class NotificationComponent implements NotificationChannel.Listener {
public static interface AnimationView {
boolean isReady();
boolean isVisible();
void setComponent(JComponent component, JComponent owner, Runnable onHide);
void close();
}
private final JPanel myComponent;
private final Action[] myActions;
int myPosition;
private Action myBackwardAction;
private Action myForwardAction;
private final Set<NotificationItem> myNotifications = new HashSet<NotificationItem>();
private final NotificationChannel myChannel;
private Action myClearAction;
private final AnimationView mySlider;
NotificationComponent(NotificationChannel channel, AnimationView slider) {
mySlider = slider;
myComponent = new JPanel(new CardLayout());
List<Action> actions = new ArrayList<Action>();
myBackwardAction = createBackwardAction();
myForwardAction = createForwardAction();
myClearAction = createClearAction();
actions.add(myBackwardAction);
actions.add(myForwardAction);
actions.add(myClearAction);
myActions = actions.toArray(new Action[0]);
myChannel = channel;
myChannel.addListener(this);
}
void processItems() {
if (myChannel.getItems().isEmpty() && myChannel.getDefaultNotification() != null) {
addNotification(myChannel.getDefaultNotification(), myChannel);
}
for (NotificationItem notification : myChannel.getItems()) {
addNotification(notification, myChannel);
}
if (!myNotifications.isEmpty()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateEnabled();
}
});
}
}
void addNotification(NotificationItem item, NotificationChannel channel) {
if (!myNotifications.contains(item)) {
addNotification(item.myTitle, item.myBody, item.myHyperlinkListener, channel);
myNotifications.add(item);
}
}
void addNotification(String title, String body, HyperlinkListener hyperlinkListener, NotificationChannel channel) {
JComponent htmlPane = createHtmlPane(GanttLanguage.getInstance().formatText("error.channel.text", title, body),
hyperlinkListener);
UIUtil.setBackgroundTree(htmlPane, channel.getColor());
htmlPane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(channel.getColor().darker()),
BorderFactory.createEmptyBorder(3, 3, 3, 3)));
myComponent.add(htmlPane, String.valueOf(myComponent.getComponentCount()));
}
private Action createBackwardAction() {
return new GPAction("updateRss.backwardItem") {
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (myPosition > 0) {
((CardLayout) myComponent.getLayout()).show(myComponent, String.valueOf(--myPosition));
updateEnabled();
}
}
};
}
private Action createClearAction() {
return new GPAction("updateRss.clear") {
@Override
public void actionPerformed(ActionEvent arg0) {
myChannel.clear();
mySlider.close();
}
};
}
private Action createForwardAction() {
return new GPAction("updateRss.forwardItem") {
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (myPosition < myComponent.getComponentCount() - 1) {
((CardLayout) myComponent.getLayout()).show(myComponent, String.valueOf(++myPosition));
updateEnabled();
}
}
};
}
private void updateEnabled() {
assert myBackwardAction != null && myForwardAction != null;
myBackwardAction.setEnabled(myPosition > 0);
myForwardAction.setEnabled(myPosition < myComponent.getComponentCount() - 1);
if (!myChannel.getItems().isEmpty()) {
myChannel.setRead(myPosition);
}
}
JComponent getComponent() {
Action[] actions = getActions();
JPanel buttonPanel = new JPanel(new GridLayout(1, actions.length, 2, 0));
for (final Action a : actions) {
JButton button = new TestGanttRolloverButton(a);
buttonPanel.add(button);
}
buttonPanel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
JPanel result = new JPanel(new BorderLayout());
result.add(myComponent, BorderLayout.CENTER);
result.add(buttonPanel, BorderLayout.NORTH);
buttonPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
return result;
}
Action[] getActions() {
return myActions;
}
static JScrollPane createHtmlPane(String html, HyperlinkListener hyperlinkListener) {
JEditorPane htmlPane = UIUtil.createHtmlPane(html, hyperlinkListener);
htmlPane.setBackground(Color.YELLOW);
htmlPane.setBorder(BorderFactory.createEmptyBorder());
Dimension htmlSize = htmlPane.getPreferredSize();
final JScrollPane scrollPane = new JScrollPane(htmlPane);
scrollPane.setAutoscrolls(false);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
scrollPane.setPreferredSize(new Dimension(Math.min(400, htmlSize.width + 50), Math.min(300, htmlSize.height + 50)));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
scrollPane.getVerticalScrollBar().setValue(0);
}
});
return scrollPane;
}
@Override
public void notificationAdded() {
processItems();
}
@Override
public void notificationRead(NotificationItem item) {
// Do nothing
}
@Override
public void channelCleared() {
myNotifications.clear();
myComponent.removeAll();
processItems();
}
}