/* 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; import biz.ganttproject.core.calendar.CalendarEvent; import biz.ganttproject.core.calendar.GPCalendar; import biz.ganttproject.core.option.ColorOption; import biz.ganttproject.core.option.DefaultColorOption; import biz.ganttproject.core.option.GPOption; import biz.ganttproject.core.option.GPOptionChangeListener; import biz.ganttproject.core.option.GPOptionGroup; import biz.ganttproject.core.time.CalendarFactory; import com.google.common.collect.Lists; import net.sourceforge.ganttproject.chart.ChartModelBase; import net.sourceforge.ganttproject.chart.ChartModelImpl; import net.sourceforge.ganttproject.chart.ChartOptionGroup; import net.sourceforge.ganttproject.chart.ChartViewState; import net.sourceforge.ganttproject.chart.GanttChart; import net.sourceforge.ganttproject.chart.ProjectCalendarDialogAction; import net.sourceforge.ganttproject.chart.export.RenderedChartImage; import net.sourceforge.ganttproject.chart.gantt.GanttChartController; import net.sourceforge.ganttproject.chart.item.CalendarChartItem; import net.sourceforge.ganttproject.chart.item.ChartItem; import net.sourceforge.ganttproject.gui.UIConfiguration; import net.sourceforge.ganttproject.gui.zoom.ZoomManager; import net.sourceforge.ganttproject.language.GanttLanguage; import net.sourceforge.ganttproject.task.CustomPropertyEvent; import net.sourceforge.ganttproject.task.Task; import net.sourceforge.ganttproject.task.TaskManager; import net.sourceforge.ganttproject.task.algorithm.RecalculateTaskScheduleAlgorithm; import net.sourceforge.ganttproject.task.dependency.TaskDependencyException; import net.sourceforge.ganttproject.task.event.TaskDependencyEvent; import net.sourceforge.ganttproject.task.event.TaskListenerAdapter; import net.sourceforge.ganttproject.task.event.TaskScheduleEvent; import net.sourceforge.ganttproject.undo.GPUndoManager; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.Date; import java.util.List; /** * Class for the graphic part of the soft */ public class GanttGraphicArea extends ChartComponentBase implements GanttChart, CustomPropertyListener, ProjectEventListener { static { Toolkit toolkit = Toolkit.getDefaultToolkit(); URL cursorResource = GanttGraphicArea.class.getClassLoader().getResource("icons/cursorpercent.gif"); Image image = toolkit.getImage(cursorResource); CHANGE_PROGRESS_CURSOR = toolkit.createCustomCursor(image, new Point(10, 5), "CursorPercent"); } public static final Cursor W_RESIZE_CURSOR = new Cursor(Cursor.W_RESIZE_CURSOR); public static final Cursor E_RESIZE_CURSOR = new Cursor(Cursor.E_RESIZE_CURSOR); public static final Cursor CHANGE_PROGRESS_CURSOR; private GanttChartController myChartComponentImpl; private final GanttTree2 tree; private final ChartModelImpl myChartModel; private final TaskManager myTaskManager; private GPUndoManager myUndoManager; private ChartViewState myViewState; private GanttPreviousState myBaseline; private final ProjectCalendarDialogAction myPublicHolidayDialogAction; private final ChartOptionGroup myStateDiffOptions; public GanttGraphicArea(GanttProject app, GanttTree2 ttree, TaskManager taskManager, ZoomManager zoomManager, GPUndoManager undoManager) { super(app.getProject(), app.getUIFacade(), zoomManager); this.setBackground(Color.WHITE); myTaskManager = taskManager; myUndoManager = undoManager; myChartModel = new ChartModelImpl(getTaskManager(), app.getTimeUnitStack(), app.getUIConfiguration()); myChartModel.addOptionChangeListener(new GPOptionChangeListener() { @Override public void optionsChanged() { repaint(); } }); myStateDiffOptions = createBaselineColorOptions(myChartModel, app.getUIConfiguration()); this.tree = ttree; myViewState = new ChartViewState(this, app.getUIFacade()); app.getUIFacade().getZoomManager().addZoomListener(myViewState); super.setStartDate(CalendarFactory.newCalendar().getTime()); myTaskManager.addTaskListener(new TaskListenerAdapter() { @Override public void taskScheduleChanged(TaskScheduleEvent e) { adjustDependencies((Task) e.getSource()); } @Override public void dependencyAdded(TaskDependencyEvent e) { adjustDependencies(e.getDependency().getDependee()); repaint(); } @Override public void dependencyRemoved(TaskDependencyEvent e) { repaint(); } private void adjustDependencies(Task task) { RecalculateTaskScheduleAlgorithm alg = myTaskManager.getAlgorithmCollection().getRecalculateTaskScheduleAlgorithm(); if (!alg.isRunning()) { try { alg.run(task); } catch (TaskDependencyException e1) { e1.printStackTrace(); } } } }); myPublicHolidayDialogAction = new ProjectCalendarDialogAction(getProject(), getUIFacade()); getProject().getTaskCustomColumnManager().addListener(this); initMouseListeners(); } @Override public GPOptionGroup getBaselineColorOptions() { return myStateDiffOptions; } @Override public ColorOption getTaskDefaultColorOption() { return myChartModel.getTaskDefaultColorOption(); } @Override public GPOptionGroup getTaskLabelOptions() { return myChartModel.getTaskLabelOptions(); } /** @return the preferred size of the panel. */ @Override public Dimension getPreferredSize() { return new Dimension(465, 600); } public ChartModelImpl getMyChartModel() { return myChartModel; } @Override public String getName() { return GanttLanguage.getInstance().getText("gantt"); } private int getHeaderHeight() { return getImplementation().getHeaderHeight(tree, tree.getTreeTable().getScrollPane().getViewport()); } /** @return an image with the gantt chart */ // TODO: 1.11 take into account flags "render this and don't render that" public BufferedImage getChart(GanttExportSettings settings) { RenderedChartImage renderedImage = (RenderedChartImage) getRenderedImage(settings); int width = renderedImage.getWidth(); int height = renderedImage.getHeight(); BufferedImage result = renderedImage.getWholeImage(); repaint(); return result; } @Override protected GPTreeTableBase getTreeTable() { return tree.getTreeTable(); } GPUndoManager getUndoManager() { return myUndoManager; } @Override protected ChartModelBase getChartModel() { return myChartModel; } @Override protected MouseListener getMouseListener() { return getChartImplementation().getMouseListener(); } @Override protected MouseMotionListener getMouseMotionListener() { return getChartImplementation().getMouseMotionListener(); } @Override public Action[] getPopupMenuActions(MouseEvent e) { List<Action> actions = Lists.newArrayList(getOptionsDialogAction(), myPublicHolidayDialogAction); actions.addAll(createToggleHolidayAction(e.getX())); return actions.toArray(new Action[actions.size()]); } private List<Action> createToggleHolidayAction(int x) { List<Action> result = Lists.newArrayList(); ChartItem chartItem = myChartModel.getChartItemWithCoordinates(x, 0); if (chartItem instanceof CalendarChartItem) { Date date = ((CalendarChartItem) chartItem).getDate(); CalendarEvent event = getProject().getActiveCalendar().getEvent(date); int dayMask = getProject().getActiveCalendar().getDayMask(date); if ((dayMask & GPCalendar.DayMask.WEEKEND) != 0) { switch (dayMask & GPCalendar.DayMask.WORKING) { case 0: if (event == null) { result.add(CalendarEventAction.addException(getProject().getActiveCalendar(), date, getUndoManager())); } break; case GPCalendar.DayMask.WORKING: result.add(CalendarEventAction.removeException(getProject().getActiveCalendar(), date, getUndoManager())); break; } } if ((dayMask & GPCalendar.DayMask.HOLIDAY) != 0) { result.add(CalendarEventAction.removeHoliday(getProject().getActiveCalendar(), date, getUndoManager())); } else { if (event == null) { result.add(CalendarEventAction.addHoliday(getProject().getActiveCalendar(), date, getUndoManager())); } } } return result; } @Override public void repaint() { if (myChartModel != null && isShowing()) { myChartModel.setHeaderHeight(getHeaderHeight()); } super.repaint(); } @Override public void setBaseline(GanttPreviousState baseline) { if (baseline == null) { setPreviousStateTasks(null); } else { setPreviousStateTasks(baseline.load()); } myBaseline = baseline; } @Override public GanttPreviousState getBaseline() { return myBaseline; } static class MouseSupport { private final ChartModelImpl myChartModel; MouseSupport(ChartModelImpl chartModel) { myChartModel = chartModel; } protected Task findTaskUnderMousePointer(int xpos, int ypos) { ChartItem chartItem = myChartModel.getChartItemWithCoordinates(xpos, ypos); return chartItem == null ? null : chartItem.getTask(); } protected ChartItem getChartItemUnderMousePoint(int xpos, int ypos) { ChartItem result = myChartModel.getChartItemWithCoordinates(xpos, ypos); return result; } } @Override protected AbstractChartImplementation getImplementation() { return getChartImplementation(); } GanttChartController getChartImplementation() { if (myChartComponentImpl == null) { myChartComponentImpl = new GanttChartController(getProject(), getUIFacade(), myChartModel, this, tree, getViewState()); } return myChartComponentImpl; } public void setPreviousStateTasks(List<GanttPreviousStateTask> tasks) { int rowHeight = myChartModel.setBaseline(tasks); tree.getTable().setRowHeight(rowHeight); } @Override public void customPropertyChange(CustomPropertyEvent event) { repaint(); } @Override public void projectModified() { // TODO Auto-generated method stub } @Override public void projectSaved() { // TODO Auto-generated method stub } @Override public void projectClosed() { repaint(); setPreviousStateTasks(null); } @Override public void projectOpened() { } @Override public void projectCreated() { // TODO Auto-generated method stub } @Override public ChartViewState getViewState() { return myViewState; } private static ChartOptionGroup createBaselineColorOptions(ChartModelImpl chartModel, final UIConfiguration projectConfig) { final ColorOption myTaskAheadOfScheduleColor = new DefaultColorOption("ganttChartStateDiffColors.taskAheadOfScheduleColor", new Color(50, 229, 50)); myTaskAheadOfScheduleColor.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { projectConfig.setEarlierPreviousTaskColor(myTaskAheadOfScheduleColor.getValue()); } }); // final ColorOption myTaskBehindScheduleColor = new DefaultColorOption("ganttChartStateDiffColors.taskBehindScheduleColor", new Color(229, 50, 50)); myTaskBehindScheduleColor.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { projectConfig.setLaterPreviousTaskColor(myTaskBehindScheduleColor.getValue()); } }); // final ColorOption myTaskOnScheduleColor = new DefaultColorOption("ganttChartStateDiffColors.taskOnScheduleColor", Color.LIGHT_GRAY); myTaskOnScheduleColor.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { projectConfig.setPreviousTaskColor(myTaskOnScheduleColor.getValue()); } }); // return new ChartOptionGroup("ganttChartStateDiffColors", new GPOption[] { myTaskOnScheduleColor, myTaskAheadOfScheduleColor, myTaskBehindScheduleColor }, chartModel.getOptionEventDispatcher()); } }