package org.matveev.pomodoro4nb.task;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;
import org.matveev.pomodoro4nb.Notificator;
import org.matveev.pomodoro4nb.utils.ValidatableAction;
import org.matveev.pomodoro4nb.controllers.AbstractController;
import org.matveev.pomodoro4nb.controls.netbeans.TapPanel;
import org.matveev.pomodoro4nb.prefs.PreferencesProvider;
import org.matveev.pomodoro4nb.timer.PomodoroTimer.State;
import org.matveev.pomodoro4nb.timer.TimerController;
import org.matveev.pomodoro4nb.timer.TimerController.StateInfo;
import org.matveev.pomodoro4nb.data.Property;
import org.matveev.pomodoro4nb.data.io.PropertiesSerializer;
import org.matveev.pomodoro4nb.data.io.PropertiesSerializerFactory;
import org.matveev.pomodoro4nb.prefs.DefaultPreferencesProvider;
import org.matveev.pomodoro4nb.task.actions.AddInterruptionAction;
import org.matveev.pomodoro4nb.task.actions.AddTaskAction;
import org.matveev.pomodoro4nb.task.actions.AddUnplannedTaskAction;
import org.matveev.pomodoro4nb.task.actions.BasicAction;
import org.matveev.pomodoro4nb.task.actions.ClearFinishedTaskAction;
import org.matveev.pomodoro4nb.task.actions.EditTaskAction;
import org.matveev.pomodoro4nb.task.actions.MarkTaskAction;
import org.matveev.pomodoro4nb.task.actions.NamedAction;
import org.matveev.pomodoro4nb.task.actions.PreferencesAction;
import org.matveev.pomodoro4nb.task.actions.PriorityAction;
import org.matveev.pomodoro4nb.task.actions.RemoveTaskAction;
import org.matveev.pomodoro4nb.utils.Base64Coder;
import org.matveev.pomodoro4nb.utils.Handler;
import org.matveev.pomodoro4nb.utils.MediaPlayer;
import org.matveev.pomodoro4nb.utils.Resources;
/**
*
* @author Alexey Matvey
*/
public class TaskController extends AbstractController {
public static final String TASK_CONTROLLER_ID = "taskController";
//
public static final Property<Boolean> HasTasksProperty = new Property<Boolean>("hasTasks", Boolean.class);
//
public static final Property<Task> TaskAddedProperty = new Property<Task>("taskAdded", Task.class);
public static final Property<Task> TaskRemovedProperty = new Property<Task>("taskRemoved", Task.class);
public static final Property<Task> ActiveTaskRemovedProperty = new Property<Task>("activeRemoved", Task.class);
public static final Property<Interruption> InterruptionAddedProperty = new Property<Interruption>("interruptionAdded", Interruption.class);
//
private List<ValidatableAction> actions = new CopyOnWriteArrayList<ValidatableAction>();
//
private final TaskTable taskTable;
private final JPopupMenu popupMenu;
//
private final PreferencesProvider provider;
//
private Task currentTask;
public TaskController(PreferencesProvider provider) {
this.provider = provider;
//<editor-fold defaultstate="collapsed" desc="Create components">
taskTable = new TaskTable();
ActionValidator validator = new ActionValidator();
taskTable.getSelectionModel().addListSelectionListener(validator);
taskTable.getModel().addTableModelListener(validator);
taskTable.addMouseListener(new DescriptionViewer(taskTable));
taskTable.addMouseListener(new RightClickSelector(taskTable));
popupMenu = createTaskTablePopupMenu();
PopupListener listener = new PopupListener(popupMenu);
taskTable.addMouseListener(listener);
taskTable.getTableHeader().addMouseListener(listener);
//</editor-fold>
registerHandler(TimerController.STATE_CHANGED_PROPERTY, new Handler<StateInfo>() {
@Override
public void handle(StateInfo oldState, StateInfo newState) {
if (newState != null && newState.isForced()) {
if (State.WORK.equals(newState.getState())) {
int index = taskTable.getSelectedRow();
if (index != -1) {
currentTask = taskTable.getTaskTableModel().getTask(index);
}
}
} else if (newState != null && !newState.isForced()) {
if (State.IDLE.equals(newState.getState())) {
Notificator.showNotificationBalloon(Notificator.KEY_START_WORK);
tryPlaySound();
} else if (State.BREAK.equals(newState.getState())) {
if (currentTask != null) {
final Integer pomodoros = currentTask.getProperty(Task.Pomodoros);
currentTask.setProperty(Task.Pomodoros, new Integer(pomodoros + 1));
taskTable.getTaskTableModel().fireTableDataChanged();
}
Notificator.showNotificationBalloon(Notificator.KEY_START_BREAK);
tryPlaySound();
}
}
}
});
}
private void tryPlaySound() {
if (Boolean.TRUE.equals(provider.getBoolean(
DefaultPreferencesProvider.ENABLE_SOUNDS_KEY, false))) {
MediaPlayer.play(Resources.getSound("budzik.wav"));
}
}
public Container createQuickActionPanel() {
final TapPanel panel = new TapPanel();
panel.setOrientation(TapPanel.DOWN);
panel.setPreferredSize(new Dimension(panel.getWidth(), 20));
final JToolBar tools = new JToolBar();
tools.setFloatable(false);
tools.setRollover(true);
addAction(tools, new AddTaskAction(taskTable));
addAction(tools, new RemoveTaskAction(taskTable, this));
addAction(tools, new EditTaskAction(taskTable));
tools.addSeparator();
addAction(tools, new MarkTaskAction(taskTable));
tools.addSeparator();
addAction(tools, new ClearFinishedTaskAction(taskTable));
addAction(tools, new PreferencesAction(provider));
panel.add(tools);
panel.add(new JPanel());
validateAcions();
return panel;
}
private JPopupMenu createTaskTablePopupMenu() {
JPopupMenu menu = new JPopupMenu();
addAction(menu, new AddTaskAction(taskTable));
addAction(menu, new RemoveTaskAction(taskTable, this));
addAction(menu, new EditTaskAction(taskTable));
menu.addSeparator();
addAction(menu, new MarkTaskAction(taskTable));
menu.addSeparator();
menu.add(createTagsSubMenu());
menu.addSeparator();
addAction(menu, new AddInterruptionAction(taskTable));
addAction(menu, new AddUnplannedTaskAction(taskTable, this));
menu.addSeparator();
addAction(menu, new ClearFinishedTaskAction(taskTable));
menu.addSeparator();
addAction(menu, new PreferencesAction(provider));
validateAcions();
return menu;
}
private JMenu createTagsSubMenu() {
JMenu tagsMenu = new JMenu(new NamedAction(taskTable, "Set tag for task"));
ValidatableAction a = (ValidatableAction) tagsMenu.getAction();
actions.add(a);
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, Task.Priority.Improvements)));
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, Task.Priority.Blocker)));
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, Task.Priority.Critical)));
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, Task.Priority.Major)));
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, Task.Priority.Minor)));
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, Task.Priority.Trivial)));
tagsMenu.addSeparator();
tagsMenu.add(new JMenuItem(new PriorityAction(taskTable, null)));
return tagsMenu;
}
private void addAction(JToolBar toolbar, ValidatableAction action) {
actions.add(action);
JButton btn = new JButton(action);
btn.setText(null);
toolbar.add(btn);
}
private void addAction(JPopupMenu menu, ValidatableAction action) {
actions.add(action);
JMenuItem item = new JMenuItem(action);
item.setIcon(null);
if (action instanceof BasicAction) {
item.setAccelerator(((BasicAction) action).getActionKeyStroke());
}
menu.add(item);
}
@Override
public Container createUI() {
return createTaskTablePanel();
}
private JScrollPane createTaskTablePanel() {
final JScrollPane scrollPane = new JScrollPane(taskTable,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
setPreferredTableColumnWidths(e.getComponent().getSize(), taskTable,
new double[]{0.6, 0.1, 0.1, 0.1, 0.1});
taskTable.revalidate();
taskTable.repaint();
}
});
scrollPane.addMouseListener(new PopupListener(popupMenu));
registerShortCuts(scrollPane);
return scrollPane;
}
private void registerShortCuts(JComponent panel) {
InputMap im = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = panel.getActionMap();
for (Action action : actions) {
if (action instanceof BasicAction) {
im.put(((BasicAction) action).getActionKeyStroke(), action.getValue(Action.NAME));
am.put(action.getValue(Action.NAME), action);
}
}
}
private void setPreferredTableColumnWidths(Dimension d, JTable table, double[] percentages) {
Dimension tableDim = d;
double total = 0;
for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) {
total += percentages[i];
}
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setPreferredWidth((int) Math.round(tableDim.width * (percentages[i] / total)));
}
}
@Override
public void store(java.util.Properties props) throws Exception {
final PropertiesSerializer serializer = PropertiesSerializerFactory.createXMLSerializer();
final Activity activity = taskTable.getTaskTableModel().getActivity();
final String data = serializer.serialize(activity);
props.setProperty("model", Base64Coder.encodeString(data));
}
@Override
public void restore(java.util.Properties props) throws Exception {
Object data = props.getProperty("model");
if (data != null) {
final PropertiesSerializer serializer = PropertiesSerializerFactory.createXMLSerializer();
final String xmlString = Base64Coder.decodeString((String) data);
Activity activity = (Activity) serializer.deserealize(xmlString.trim());
taskTable.setModel(new TaskTableModel(activity != null ? activity : new Activity()));
}
}
public Task getCurretTask() {
return currentTask;
}
private static final class PopupListener extends MouseAdapter {
private final JPopupMenu popup;
public PopupListener(JPopupMenu popup) {
this.popup = popup;
}
@Override
public void mousePressed(MouseEvent e) {
showPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
showPopup(e);
}
private void showPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
private void validateAcions() {
for (ValidatableAction a : actions) {
a.validate();
}
}
private final class ActionValidator implements TableModelListener, ListSelectionListener {
public ActionValidator() {
}
@Override
public void tableChanged(TableModelEvent e) {
validateAcions();
updateProperties(e);
}
@Override
public void valueChanged(ListSelectionEvent e) {
validateAcions();
}
private void updateProperties(final EventObject event) {
if (event.getSource() instanceof JTable) {
putProperty(TaskController.HasTasksProperty,
((JTable) event.getSource()).getRowCount() > 0);
}
}
}
private static final class DescriptionViewer extends MouseAdapter {
private final TaskTable table;
public DescriptionViewer(TaskTable table) {
this.table = table;
}
@Override
public void mouseMoved(MouseEvent e) {
final Point p = e.getPoint();
final TaskTableModel model = table.getTaskTableModel();
Task task = (Task) model.getValueAt(table.rowAtPoint(p), table.columnAtPoint(p));
table.setToolTipText(task.getProperty(Task.Description));
}
}
private static final class RightClickSelector extends MouseAdapter {
private final TaskTable table;
public RightClickSelector(TaskTable table) {
this.table = table;
}
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
final int rowNumber = table.rowAtPoint(e.getPoint());
final ListSelectionModel model = table.getSelectionModel();
model.setSelectionInterval(rowNumber, rowNumber);
}
}
}
}