/* * This file is part of NavalPlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package org.zkoss.ganttz; import static org.zkoss.ganttz.i18n.I18nHelper._; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.zkoss.ganttz.adapters.IDisabilityConfiguration; import org.zkoss.ganttz.adapters.IDomainAndBeansMapper; import org.zkoss.ganttz.adapters.PlannerConfiguration; import org.zkoss.ganttz.data.Dependency; import org.zkoss.ganttz.data.GanttDiagramGraph; import org.zkoss.ganttz.data.GanttDiagramGraph.GanttZKDiagramGraph; import org.zkoss.ganttz.data.GanttDiagramGraph.IGraphChangeListener; import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.extensions.ICommand; import org.zkoss.ganttz.extensions.ICommandOnTask; import org.zkoss.ganttz.extensions.IContext; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.TimeTrackerComponent; import org.zkoss.ganttz.timetracker.TimeTrackerComponentWithoutColumns; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.ComponentsFinder; import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.ganttz.util.LongOperationFeedback.ILongOperation; import org.zkoss.ganttz.util.WeakReferencedListeners; import org.zkoss.ganttz.util.WeakReferencedListeners.IListenerNotification; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.HtmlMacroComponent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Button; import org.zkoss.zul.ListModel; import org.zkoss.zul.Listbox; import org.zkoss.zul.Listitem; import org.zkoss.zul.Separator; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.South; public class Planner extends HtmlMacroComponent { public static boolean guessContainersExpandedByDefaultGivenPrintParameters( Map printParameters) { return guessContainersExpandedByDefault(convertToURLParameters(printParameters)); } private static Map convertToURLParameters( Map printParameters) { Map result = new HashMap(); for (Entry each : printParameters.entrySet()) { result.put(each.getKey(), new String[] { each.getValue() }); } return result; } public static boolean guessContainersExpandedByDefault( Map queryURLParameters) { String[] values = queryURLParameters.get("expanded"); if (values == null) { return false; } return toLowercaseSet(values).contains("all"); } private static Set toLowercaseSet(String[] values) { Set result = new HashSet(); for (String each : values) { result.add(each.toLowerCase()); } return result; } private GanttZKDiagramGraph diagramGraph; private LeftPane leftPane; private GanttPanel ganttPanel; private boolean fixedZoomByUser = false; private List> contextualizedGlobalCommands; private CommandContextualized goingDownInLastArrowCommand; private List> commandsOnTasksContextualized; private CommandOnTaskContextualized doubleClickCommand; private FunctionalityExposedForExtensions context; private transient IDisabilityConfiguration disabilityConfiguration; private boolean isShowingCriticalPath = false; private boolean isShowingLabels = false; private boolean isShowingResources = false; private boolean isExpandAll = false; private boolean isFlattenTree = false; private ZoomLevel initialZoomLevel = null; private Listbox listZoomLevels = null; private WeakReferencedListeners chartVisibilityListeners = WeakReferencedListeners .create(); public Planner() { } TaskList getTaskList() { if (ganttPanel == null) { return null; } List children = ganttPanel.getChildren(); return ComponentsFinder.findComponentsOfType(TaskList.class, children).get(0); } public int getTaskNumber() { return getTaskList().getTasksNumber(); } private static int PIXELS_PER_TASK_LEVEL = 21; private static int PIXELS_PER_CHARACTER = 5; public int calculateMinimumWidthForTaskNameColumn(boolean expand) { return calculateMinimumWidthForTaskNameColumn(expand, getTaskList().getAllTasks()); } private int calculateMinimumWidthForTaskNameColumn(boolean expand, List tasks) { IDomainAndBeansMapper mapper = getContext().getMapper(); int widest = 0; for(Task task : tasks) { int numberOfAncestors = mapper.findPositionFor(task).getAncestors().size(); int numberOfCharacters = task.getName().length(); widest = Math.max(widest, numberOfCharacters * PIXELS_PER_CHARACTER + numberOfAncestors * PIXELS_PER_TASK_LEVEL); if(expand && !task.isLeaf()) { widest = Math.max(widest, calculateMinimumWidthForTaskNameColumn(expand, task.getTasks())); } } return widest; } public int getAllTasksNumber() { return diagramGraph.getTasks().size(); } public String getContextPath() { return Executions.getCurrent().getContextPath(); } public DependencyList getDependencyList() { if (ganttPanel == null) { return null; } List children = ganttPanel.getChildren(); List found = ComponentsFinder.findComponentsOfType(DependencyList.class, children); if (found.isEmpty()) { return null; } return found.get(0); } public void addTasks(Position position, Collection newTasks) { TaskList taskList = getTaskList(); if (taskList != null && leftPane != null) { taskList.addTasks(position, newTasks); leftPane.addTasks(position, newTasks); } } public void addTask(Position position, Task task) { addTasks(position, Arrays.asList(task)); } void addDependencies(Collection dependencies) { DependencyList dependencyList = getDependencyList(); if (dependencyList == null) { return; } for (DependencyComponent d : getTaskList().asDependencyComponents( dependencies)) { dependencyList.addDependencyComponent(d); } } public ListModel getZoomLevels() { ZoomLevel[] selectableZoomlevels = { ZoomLevel.DETAIL_ONE, ZoomLevel.DETAIL_TWO, ZoomLevel.DETAIL_THREE, ZoomLevel.DETAIL_FOUR, ZoomLevel.DETAIL_FIVE }; return new SimpleListModel(selectableZoomlevels); } public void setZoomLevel(final ZoomLevel zoomLevel) { if (ganttPanel == null) { return; } this.fixedZoomByUser = true; initialZoomLevel = zoomLevel; ganttPanel.setZoomLevel(zoomLevel); } public void zoomIncrease() { if (ganttPanel == null) { return; } LongOperationFeedback.execute(ganttPanel, new ILongOperation() { @Override public String getName() { return _("increasing zoom"); } @Override public void doAction() throws Exception { ganttPanel.zoomIncrease(); } }); } public void zoomDecrease() { if (ganttPanel == null) { return; } LongOperationFeedback.execute(ganttPanel, new ILongOperation() { @Override public String getName() { return _("decreasing zoom"); } @Override public void doAction() throws Exception { ganttPanel.zoomDecrease(); } }); } public void setConfiguration(PlannerConfiguration configuration) { if (configuration == null) { return; } this.diagramGraph = GanttDiagramGraph.create(configuration .getStartConstraints(), configuration.getEndConstraints(), configuration.isDependenciesConstraintsHavePriority()); FunctionalityExposedForExtensions newContext = new FunctionalityExposedForExtensions( this, configuration, diagramGraph); addGraphChangeListenersFromConfiguration(configuration); this.contextualizedGlobalCommands = contextualize(newContext, configuration.getGlobalCommands()); this.commandsOnTasksContextualized = contextualize(newContext, configuration.getCommandsOnTasks()); goingDownInLastArrowCommand = contextualize(newContext, configuration .getGoingDownInLastArrowCommand()); doubleClickCommand = contextualize(newContext, configuration .getDoubleClickCommand()); this.context = newContext; this.disabilityConfiguration = configuration; resettingPreviousComponentsToNull(); newContext.add(configuration.getData()); setupComponents(); setAt("insertionPointLeftPanel", leftPane); leftPane.afterCompose(); setAt("insertionPointRightPanel", ganttPanel); ganttPanel.afterCompose(); leftPane.setGoingDownInLastArrowCommand(goingDownInLastArrowCommand); TimeTrackerComponent timetrackerheader = new TimeTrackerComponentWithoutColumns( ganttPanel.getTimeTracker(), "timetrackerheader"); setAt("insertionPointTimetracker", timetrackerheader); timetrackerheader.afterCompose(); Component chartComponent = configuration.getChartComponent(); if (chartComponent != null) { setAt("insertionPointChart", chartComponent); } if (!configuration.isCriticalPathEnabled()) { Button showCriticalPathButton = (Button) getFellow("showCriticalPath"); showCriticalPathButton.setVisible(false); } if (!configuration.isExpandAllEnabled()) { Button expandAllButton = (Button) getFellow("expandAll"); expandAllButton.setVisible(false); } if (!configuration.isFlattenTreeEnabled()) { Button flattenTree = (Button) getFellow("flattenTree"); flattenTree.setVisible(false); } listZoomLevels.setSelectedIndex(getZoomLevel().ordinal()); this.visibleChart = configuration.isExpandPlanningViewCharts(); ((South) getFellow("graphics")).setOpen(this.visibleChart); } private void resettingPreviousComponentsToNull() { this.ganttPanel = null; this.leftPane = null; } private void setAt(String insertionPointId, Component component) { Component insertionPoint = getFellow(insertionPointId); insertionPoint.getChildren().clear(); insertionPoint.appendChild(component); } private List> contextualize( FunctionalityExposedForExtensions context, List> commands) { List> result = new ArrayList>(); for (ICommandOnTask c : commands) { result.add(contextualize(context, c)); } return result; } private CommandOnTaskContextualized contextualize( FunctionalityExposedForExtensions context, ICommandOnTask commandOnTask) { return CommandOnTaskContextualized.create(commandOnTask, context .getMapper(), context); } private CommandContextualized contextualize(IContext context, ICommand command) { if (command == null) { return null; } return CommandContextualized.create(command, context); } private List> contextualize( IContext context, Collection> commands) { ArrayList> result = new ArrayList>(); for (ICommand command : commands) { result.add(contextualize(context, command)); } return result; } private void setupComponents() { insertGlobalCommands(); predicate = new FilterAndParentExpandedPredicates(context) { @Override public boolean accpetsFilterPredicate(Task task) { return true; } }; this.leftPane = new LeftPane(disabilityConfiguration, this.diagramGraph .getTopLevelTasks(), predicate); this.ganttPanel = new GanttPanel(this, commandsOnTasksContextualized, doubleClickCommand, disabilityConfiguration, predicate); Button button = (Button) getFellow("btnPrint"); button.setDisabled(!context.isPrintEnabled()); } @SuppressWarnings("unchecked") private void insertGlobalCommands() { Component toolbar = getToolbar(); Component firstSeparator = getFirstSeparatorFromToolbar(); toolbar.getChildren().removeAll(getBefore(toolbar, firstSeparator)); for (CommandContextualized c : contextualizedGlobalCommands) { toolbar.insertBefore(c.toButton(), firstSeparator); } } @SuppressWarnings("unchecked") private List getBefore(Component parent, Component child) { List children = parent.getChildren(); List result = new ArrayList(); for (Component object : children) { if (object == child) { break; } result.add(object); } return result; } @SuppressWarnings("unchecked") private Component getFirstSeparatorFromToolbar() { Component toolbar = getToolbar(); List children = toolbar.getChildren(); List separators = ComponentsFinder .findComponentsOfType( Separator.class, children); return separators.get(0); } private Component getToolbar() { Component toolbar = getFellow("toolbar"); return toolbar; } void removeTask(Task task) { TaskList taskList = getTaskList(); taskList.remove(task); getDependencyList().taskRemoved(task); leftPane.taskRemoved(task); setHeight(getHeight());// forcing smart update ganttPanel.adjustZoomColumnsHeight(); getDependencyList().redrawDependencies(); } @Override public void afterCompose() { super.afterCompose(); listZoomLevels = (Listbox) getFellow("listZoomLevels"); Component westContainer = getFellow("taskdetailsContainer"); westContainer.addEventListener(Events.ON_SIZE, new EventListener() { @Override public void onEvent(Event event) throws Exception { Clients.evalJavaScript("zkTaskContainer.legendResize();"); } }); } public TimeTracker getTimeTracker() { return ganttPanel.getTimeTracker(); } private IGraphChangeListener showCriticalPathOnChange = new IGraphChangeListener() { @Override public void execute() { context.showCriticalPath(); } }; private boolean containersExpandedByDefault = false; private FilterAndParentExpandedPredicates predicate; private boolean visibleChart; public void showCriticalPath() { Button showCriticalPathButton = (Button) getFellow("showCriticalPath"); if (disabilityConfiguration.isCriticalPathEnabled()) { if (isShowingCriticalPath) { context.hideCriticalPath(); diagramGraph.removePostGraphChangeListener(showCriticalPathOnChange); showCriticalPathButton.setSclass("planner-command"); showCriticalPathButton.setTooltiptext(_("Show critical path")); } else { context.showCriticalPath(); diagramGraph.addPostGraphChangeListener(showCriticalPathOnChange); showCriticalPathButton.setSclass("planner-command clicked"); showCriticalPathButton.setTooltiptext(_("Hide critical path")); } isShowingCriticalPath = !isShowingCriticalPath; } } public void showAllLabels() { Button showAllLabelsButton = (Button) getFellow("showAllLabels"); if (isShowingLabels) { Clients.evalJavaScript("zkTasklist.hideAllTooltips();"); showAllLabelsButton.setSclass("planner-command show-labels"); } else { Clients.evalJavaScript("zkTasklist.showAllTooltips();"); showAllLabelsButton .setSclass("planner-command show-labels clicked"); } isShowingLabels = !isShowingLabels; } public void showAllResources() { Button showAllLabelsButton = (Button) getFellow("showAllResources"); if (isShowingResources) { Clients.evalJavaScript("zkTasklist.hideResourceTooltips();"); showAllLabelsButton.setSclass("planner-command show-resources"); } else { Clients.evalJavaScript("zkTasklist.showResourceTooltips();"); showAllLabelsButton .setSclass("planner-command show-resources clicked"); } isShowingResources = !isShowingResources; } public void print() { // Pending to raise print configuration popup. Information retrieved // should be passed as parameter to context print method context.print(); } public ZoomLevel getZoomLevel() { if (ganttPanel == null) { return initialZoomLevel != null ? initialZoomLevel : ZoomLevel.DETAIL_ONE; } return ganttPanel.getTimeTracker().getDetailLevel(); } public boolean isFixedZoomByUser() { return this.fixedZoomByUser; } public void setInitialZoomLevel(final ZoomLevel zoomLevel) { if (this.initialZoomLevel != null) { // already initialized return; } this.initialZoomLevel = zoomLevel; } public boolean areContainersExpandedByDefault() { return containersExpandedByDefault; } public void setAreContainersExpandedByDefault( boolean containersExpandedByDefault) { this.containersExpandedByDefault = containersExpandedByDefault; } public void expandAll() { Button expandAllButton = (Button) getFellow("expandAll"); if (disabilityConfiguration.isExpandAllEnabled()) { if (isExpandAll) { context.collapseAll(); expandAllButton.setSclass("planner-command"); } else { context.expandAll(); expandAllButton.setSclass("planner-command clicked"); } } isExpandAll = !isExpandAll; } public void expandAllAlways() { Button expandAllButton = (Button) getFellow("expandAll"); if (disabilityConfiguration.isExpandAllEnabled()) { context.expandAll(); expandAllButton.setSclass("planner-command clicked"); } } public void updateSelectedZoomLevel() { if (!isFixedZoomByUser()) { Listitem selectedItem = (Listitem) listZoomLevels.getItems().get( initialZoomLevel.ordinal()); listZoomLevels.setSelectedItem(selectedItem); listZoomLevels.invalidate(); } } public IContext getContext() { return context; } public void setTaskListPredicate(FilterAndParentExpandedPredicates predicate) { this.predicate = predicate; leftPane.setPredicate(predicate); getTaskList().setPredicate(predicate); getDependencyList().redrawDependencies(); if (isShowingLabels) { Clients.evalJavaScript("zkTasklist.showAllTooltips();"); } if (isShowingResources) { Clients.evalJavaScript("zkTasklist.showResourceTooltips();"); } } public void flattenTree() { Button flattenTreeButton = (Button) getFellow("flattenTree"); if (disabilityConfiguration.isFlattenTreeEnabled()) { if (isFlattenTree) { predicate.setFilterContainers(false); flattenTreeButton.setSclass("planner-command"); } else { predicate.setFilterContainers(true); flattenTreeButton.setSclass("planner-command clicked"); } setTaskListPredicate(predicate); } isFlattenTree = !isFlattenTree; } public FilterAndParentExpandedPredicates getPredicate() { return predicate; } public void changeChartVisibility(boolean visible) { visibleChart = visible; chartVisibilityListeners .fireEvent(new IListenerNotification() { @Override public void doNotify( IChartVisibilityChangedListener listener) { listener.chartVisibilityChanged(visibleChart); } }); } public boolean isVisibleChart() { return visibleChart; } public void addChartVisibilityListener( IChartVisibilityChangedListener chartVisibilityChangedListener) { chartVisibilityListeners.addListener(chartVisibilityChangedListener); } public void addGraphChangeListenersFromConfiguration( PlannerConfiguration configuration) { diagramGraph.addPreChangeListeners(configuration .getPreChangeListeners()); diagramGraph.addPostChangeListeners(configuration .getPostChangeListeners()); } public boolean isShowingCriticalPath() { return isShowingCriticalPath; } public boolean isShowingLabels() { return isShowingLabels; } public boolean isShowingResources() { return isShowingResources; } public boolean isExpandAll() { return isExpandAll; } public boolean isFlattenTree() { return isFlattenTree; } public Button findCommandComponent(String name) { for (CommandContextualized c : contextualizedGlobalCommands) { if (c.getCommand().getName().equals(name)) { return c.toButton(); } } return null; } public String getWidgetClass(){ return getDefinition().getDefaultWidgetClass(); } }