/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * * 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 <http://www.gnu.org/licenses/>. */ package org.zkoss.ganttz; import static org.zkoss.ganttz.i18n.I18nHelper._; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import org.zkoss.ganttz.data.Dependency; import org.zkoss.ganttz.data.DependencyType; import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.TimeTrackerComponent; import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.ComponentsFinder; import org.zkoss.ganttz.util.MenuBuilder; import org.zkoss.ganttz.util.MenuBuilder.ItemAction; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.ext.AfterCompose; import org.zkoss.zul.Menupopup; import org.zkoss.zul.Messagebox; import org.zkoss.zul.impl.XulElement; /** * @author Francisco Javier Moran Rúa <jmoran@igalia.com> * @author Lorenzo Tilve Álvaro <ltilve@igalia.com> */ public class DependencyList extends XulElement implements AfterCompose { private final class ChangeTypeAction implements ItemAction<DependencyComponent> { private final DependencyType type; private ChangeTypeAction(DependencyType type) { this.type = type; } @Override public void onEvent(final DependencyComponent chosen, Event event) { boolean canBeAdded = context.changeType(chosen.getDependency(), type); if ( !canBeAdded ) { warnUser(_("The specified dependency is not allowed")); } } private void warnUser(String message) { Messagebox.show(message, null, Messagebox.OK, Messagebox.EXCLAMATION, 0, null); } } private final class DependencyVisibilityToggler implements PropertyChangeListener { private final Task source; private final Task destination; private final DependencyComponent dependencyComponent; private DependencyVisibilityToggler(Task source, Task destination, DependencyComponent dependencyComponent) { this.source = source; this.destination = destination; this.dependencyComponent = dependencyComponent; } @Override public void propertyChange(PropertyChangeEvent evt) { if ( !"visible".equals(evt.getPropertyName()) ) { return; } if ( dependencyMustBeVisible() != isDependencyNowVisible() ) { toggleDependencyExistence(dependencyMustBeVisible()); } } void toggleDependencyExistence(boolean visible) { if ( visible ) { appendChild(dependencyComponent); dependencyComponent.afterCompose(); addContextMenu(dependencyComponent); } else { removeChild(dependencyComponent); } } boolean isDependencyNowVisible() { return dependencyComponent.getParent() != null; } boolean dependencyMustBeVisible() { return source.isVisible() && destination.isVisible(); } } private transient IZoomLevelChangedListener listener; private final FunctionalityExposedForExtensions<?> context; private Menupopup contextMenu; private Menupopup limitingContextMenu; public DependencyList(FunctionalityExposedForExtensions<?> context) { this.context = context; } private List<DependencyComponent> getDependencyComponents() { List<Component> children = getChildren(); return ComponentsFinder.findComponentsOfType(DependencyComponent.class, children); } void addDependencyComponent(final DependencyComponent dependencyComponent) { TaskComponent source = dependencyComponent.getSource(); TaskComponent destination = dependencyComponent.getDestination(); DependencyVisibilityToggler visibilityToggler = new DependencyVisibilityToggler(source.getTask(), destination.getTask(), dependencyComponent); source.getTask().addVisibilityPropertiesChangeListener(visibilityToggler); destination.getTask().addVisibilityPropertiesChangeListener(visibilityToggler); dependencyComponent.setVisibilityChangeListener(visibilityToggler); boolean dependencyMustBeVisible = visibilityToggler.dependencyMustBeVisible(); visibilityToggler.toggleDependencyExistence(dependencyMustBeVisible); if ( dependencyMustBeVisible ) { dependencyComponent.redrawDependency(); } } private void addContextMenu(DependencyComponent dependencyComponent) { Menupopup contextMenu = dependencyComponent.hasLimitingTasks() ? getLimitingContextMenu() : getContextMenu(); dependencyComponent.setContext(contextMenu); } private GanttPanel getGanttPanel() { return (GanttPanel) getParent(); } void setDependencyComponents(List<DependencyComponent> dependencyComponents) { for (DependencyComponent dependencyComponent : dependencyComponents) { addDependencyComponent(dependencyComponent); } } @Override public void afterCompose() { if ( listener == null ) { /* Do not replace it with lambda */ listener = new IZoomLevelChangedListener() { @Override public void zoomLevelChanged(ZoomLevel detailLevel) { if ( !isInPage() ) { return; } for (DependencyComponent dependencyComponent : getDependencyComponents()) { dependencyComponent.zoomChanged(); } } }; getTimeTracker().addZoomListener(listener); } addContextMenu(); } private boolean isInPage() { return getParent() != null && getGanttPanel() != null && getGanttPanel().getParent() != null; } private TimeTracker getTimeTracker() { return getTimeTrackerComponent().getTimeTracker(); } private void addContextMenu() { for (DependencyComponent dependencyComponent : getDependencyComponents()) { addContextMenu(dependencyComponent); } } private Menupopup getLimitingContextMenu() { if ( limitingContextMenu == null ) { MenuBuilder<DependencyComponent> contextMenuBuilder = MenuBuilder.on(getPage(), getDependencyComponents()).item( _("Erase"), "/common/img/ico_borrar.png", (chosen, event) -> context.removeDependency(chosen.getDependency())); limitingContextMenu = contextMenuBuilder.create(); } return limitingContextMenu; } private Menupopup getContextMenu() { if ( contextMenu == null ) { MenuBuilder<DependencyComponent> contextMenuBuilder = MenuBuilder.on(getPage(), getDependencyComponents()).item( _("Erase"), "/common/img/ico_borrar.png", ((chosen, event) -> context.removeDependency(chosen.getDependency()))); contextMenuBuilder.item(_("Set End-Start"), null, new ChangeTypeAction(DependencyType.END_START)); contextMenuBuilder.item(_("Set Start-Start"), null, new ChangeTypeAction(DependencyType.START_START)); contextMenuBuilder.item(_("Set End-End"), null, new ChangeTypeAction(DependencyType.END_END)); contextMenu = contextMenuBuilder.create(); } return contextMenu; } private TimeTrackerComponent getTimeTrackerComponent() { return getGanttPanel().getTimeTrackerComponent(); } void redrawDependencies() { redrawDependencyComponents(getDependencyComponents()); } private void redrawDependencyComponents(List<DependencyComponent> dependencyComponents) { for (DependencyComponent dependencyComponent : dependencyComponents) { dependencyComponent.redrawDependency(); } } void taskRemoved(Task task) { for (DependencyComponent dependencyComponent : DependencyList.this.getDependencyComponents()) { if ( dependencyComponent.contains(task) ) { removeDependencyComponent(dependencyComponent); } } } public void remove(Dependency dependency) { for (DependencyComponent dependencyComponent : DependencyList.this.getDependencyComponents()) { if ( dependencyComponent.hasSameSourceAndDestination(dependency) ) { removeDependencyComponent(dependencyComponent); } } } private void removeDependencyComponent(DependencyComponent dependencyComponent) { // Remove the visibility listener attached to the tasks TaskComponent source = dependencyComponent.getSource(); TaskComponent destination = dependencyComponent.getDestination(); PropertyChangeListener listener = dependencyComponent.getVisibilityChangeListener(); source.getTask().removeVisibilityPropertiesChangeListener(listener); destination.getTask().removeVisibilityPropertiesChangeListener(listener); // Remove other change listeners dependencyComponent.removeChangeListeners(); // Remove the dependency itself this.removeChild(dependencyComponent); } }