/*
* Copyright (c) Henrik Niehaus
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the project (Lazy Bones) nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package lazybones.gui.components.timeline;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Locale;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import lazybones.ChannelManager;
import lazybones.ChannelManager.ChannelNotFoundException;
import lazybones.LazyBones;
import lazybones.LazyBonesTimer;
import lazybones.conflicts.Conflict;
import lazybones.programmanager.ProgramManager;
import lazybones.utils.Period;
import lazybones.utils.Utilities;
import devplugin.Channel;
public class TimelineElement extends JComponent implements MouseListener {
private LazyBonesTimer timer;
private Calendar currentDate;
private Calendar timelineStart;
private Calendar timelineEnd;
private final DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault());
public final static Color COLOR_ACTIVE = UIManager.getColor("List.selectionBackground");
public final static Color COLOR_INACTIVE = Color.LIGHT_GRAY;
public final static Color TEXT_COLOR = UIManager.getColor("List.selectionForeground");
public final static Color TEXT_COLOR_INACTIVE = Color.GRAY;
public final static Color CONFLICT_COLOR = new Color(255, 0, 0, 90);
public TimelineElement(LazyBonesTimer timer, Calendar currentDate) {
this.timer = timer;
this.addMouseListener(this);
setCurrentDate(currentDate);
// set tooltip
setToolTipText(createToolTipText(timer));
}
private String createToolTipText(LazyBonesTimer timer) {
String title = timer.getDisplayTitle().replace("|", "");
DateFormat dateFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault());
StringBuffer sb = new StringBuffer();
sb.append("<html>");
sb.append("<b>");
sb.append(title);
sb.append("</b><br>");
Channel chan;
try {
chan = ChannelManager.getInstance().getTvbrowserChannel(timer);
sb.append(chan.getName());
} catch (ChannelNotFoundException e) {
sb.append(timer.getChannelNumber());
}
sb.append(' ');
sb.append(dateFormatter.format(timer.getStartTime().getTime()));
sb.append(" - ");
sb.append(dateFormatter.format(timer.getEndTime().getTime()));
sb.append("<br>");
sb.append("<br>");
sb.append("<div width=\"300\">");
sb.append(timer.getDescription());
sb.append("</div>");
sb.append("</html>");
return sb.toString();
}
public LazyBonesTimer getTimer() {
return timer;
}
public void setTimer(LazyBonesTimer timer) {
this.timer = timer;
}
public Calendar getCurrentDate() {
return currentDate;
}
public void setCurrentDate(Calendar currentDate) {
this.currentDate = currentDate;
int startHour = Integer.parseInt(LazyBones.getProperties().getProperty("timelineStartHour"));
timelineStart = (Calendar) currentDate.clone();
timelineStart.set(Calendar.HOUR_OF_DAY, startHour);
timelineEnd = (Calendar) timelineStart.clone();
timelineEnd.add(Calendar.DAY_OF_MONTH, 1);
}
@Override
public Border getBorder() {
if (timer.isActive()) {
return BorderFactory.createBevelBorder(BevelBorder.RAISED, COLOR_ACTIVE, COLOR_ACTIVE.darker());
} else {
return BorderFactory.createBevelBorder(BevelBorder.RAISED, COLOR_INACTIVE, COLOR_INACTIVE.darker());
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// enable font anti aliasing
Graphics2D g2d = (Graphics2D) g;
// storing original anitalising flag
Object state = g2d.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING);
if (state != RenderingHints.VALUE_TEXT_ANTIALIAS_ON) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
// paint background
g.setColor(timer.isActive() ? COLOR_ACTIVE : COLOR_INACTIVE);
g.fillRect(0, 0, getWidth(), getHeight());
// paint text
g.setColor(timer.isActive() ? TEXT_COLOR : TEXT_COLOR_INACTIVE);
g.setFont(new Font("SansSerif", Font.PLAIN, 9));
g.drawString(timer.getTitle(), 5, 12);
String time = df.format(timer.getStartTime().getTime()) + " - " + df.format(timer.getEndTime().getTime());
g.drawString(time, 5, 25);
// paint recording indicator
if (timer.isRecording()) {
g.setColor(Color.RED);
g.fillOval(5, 30, 7, 7);
}
// paint conflicts
g.setColor(CONFLICT_COLOR);
if (!timer.getConflicts().isEmpty()) {
for (Conflict conflict : timer.getConflicts()) {
Period period = conflict.getPeriod();
Calendar conflictStart = (Calendar) period.getStartTime().clone();
Calendar conflictEnd = (Calendar) period.getEndTime().clone();
Calendar timerStart = (Calendar) timer.getStartTime().clone();
Calendar timerEnd = (Calendar) timer.getEndTime().clone();
if (conflictStart.before(timelineStart)) {
conflictStart.setTime(timelineStart.getTime());
}
if (conflictStart.before(timerStart)) {
conflictStart.setTime(timerStart.getTime());
}
if (conflictEnd.after(timelineEnd)) {
conflictEnd.setTime(timelineEnd.getTime());
}
if (timerStart.before(timelineStart)) {
timerStart.setTime(timelineStart.getTime());
}
if (timerEnd.after(timelineEnd)) {
timerEnd.setTime(timelineEnd.getTime());
}
long durationMinutes = Utilities.getDiffInMinutes(timerStart, timerEnd);
double pixelsPerMinute = (double) getWidth() / (double) durationMinutes;
long startMinute = Utilities.getDiffInMinutes(timerStart, conflictStart);
int x = (int) Math.ceil(pixelsPerMinute * startMinute);
int width = (int) (pixelsPerMinute * Utilities.getDiffInMinutes(conflictStart, conflictEnd));
g.fillRect(x, 0, width, getHeight() - 1);
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() >= 2) {
ProgramManager.getInstance().handleTimerDoubleClick(getTimer(), e);
}
}
@Override
public void mouseEntered(MouseEvent e) {
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
@Override
public void mouseExited(MouseEvent e) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
@Override
public void mousePressed(MouseEvent e) {
mayTriggerPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
mayTriggerPopup(e);
}
/**
* Checks, if the mouse event is a popup trigger for this OS and shows the popup menu in case it is a trigger
*
* @param e
* MouseEvent to check
*/
private void mayTriggerPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
JPopupMenu popup = ProgramManager.getInstance().getContextMenuForTimer(timer);
popup.setLocation(e.getPoint());
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}