com.esofthead.mycollab.module.project.view.assignments.gantt.GanttTreeTable.java Source code

Java tutorial

Introduction

Here is the source code for com.esofthead.mycollab.module.project.view.assignments.gantt.GanttTreeTable.java

Source

/**
 * This file is part of mycollab-web.
 *
 * mycollab-web 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.
 *
 * mycollab-web 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 mycollab-web.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.esofthead.mycollab.module.project.view.assignments.gantt;

import com.esofthead.mycollab.common.i18n.GenericI18Enum;
import com.esofthead.mycollab.core.MyCollabException;
import com.esofthead.mycollab.core.utils.DateTimeUtils;
import com.esofthead.mycollab.eventmanager.ApplicationEventListener;
import com.esofthead.mycollab.eventmanager.EventBusFactory;
import com.esofthead.mycollab.module.project.CurrentProjectVariables;
import com.esofthead.mycollab.module.project.ProjectRolePermissionCollections;
import com.esofthead.mycollab.module.project.ProjectTypeConstants;
import com.esofthead.mycollab.module.project.domain.*;
import com.esofthead.mycollab.module.project.events.BugEvent;
import com.esofthead.mycollab.module.project.events.GanttEvent;
import com.esofthead.mycollab.module.project.events.MilestoneEvent;
import com.esofthead.mycollab.module.project.events.TaskEvent;
import com.esofthead.mycollab.module.project.service.GanttAssignmentService;
import com.esofthead.mycollab.module.project.service.ProjectTaskService;
import com.esofthead.mycollab.module.project.ui.components.HumanTimeConverter;
import com.esofthead.mycollab.spring.ApplicationContextUtil;
import com.esofthead.mycollab.vaadin.AppContext;
import com.esofthead.mycollab.vaadin.ui.ELabel;
import com.esofthead.mycollab.vaadin.ui.NotificationUtil;
import com.esofthead.mycollab.vaadin.web.ui.ConfirmDialogExt;
import com.esofthead.mycollab.vaadin.web.ui.field.DefaultViewField;
import com.esofthead.mycollab.vaadin.web.ui.field.converter.LocalDateConverter;
import com.google.common.eventbus.Subscribe;
import com.vaadin.data.Container;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.event.FieldEvents;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.*;
import com.vaadin.ui.themes.ValoTheme;
import org.apache.commons.collections.CollectionUtils;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.dialogs.ConfirmDialog;
import org.vaadin.peter.contextmenu.ContextMenu;

import java.util.*;

/**
 * @author MyCollab Ltd
 * @since 5.1.1
 */
public class GanttTreeTable extends TreeTable {
    private static Logger LOG = LoggerFactory.getLogger(GanttTreeTable.class);

    private GanttExt gantt;
    private GanttItemContainer beanContainer;

    private boolean ganttIndexIsChanged = false;
    protected List<Field> fields = new ArrayList<>();
    private boolean isStartedGanttChart = false;

    private ApplicationEventListener<GanttEvent.UpdateGanttItem> updateTaskInfoHandler = new ApplicationEventListener<GanttEvent.UpdateGanttItem>() {
        @Subscribe
        @Override
        public void handle(GanttEvent.UpdateGanttItem event) {
            GanttItemWrapper item = (GanttItemWrapper) event.getData();
            updateTaskTree(item);
        }
    };

    //The limit sub tasks should be less than 50, need to be calculated in the future when the project has many tasks
    private int currentPageLength = 50;

    public GanttTreeTable(final GanttExt gantt) {
        super();
        this.gantt = gantt;
        this.setWidth("800px");
        this.setBuffered(true);
        beanContainer = gantt.getBeanContainer();
        this.setContainerDataSource(beanContainer);
        this.setVisibleColumns("ganttIndex", "name", "startDate", "endDate", "duration", "percentageComplete",
                "predecessors", "assignUser");
        this.setColumnHeader("ganttIndex", "");
        this.setColumnWidth("ganttIndex", 25);
        this.setColumnHeader("name", "Task");
        this.setColumnExpandRatio("name", 1.0f);
        this.setHierarchyColumn("name");
        this.setColumnHeader("startDate", "Start");
        this.setColumnWidth("startDate", 90);
        this.setColumnHeader("endDate", "End");
        this.setColumnWidth("endDate", 90);
        this.setColumnHeader("duration", "Duration");
        this.setColumnWidth("duration", 65);
        this.setColumnHeader("predecessors", "Predecessors");
        this.setColumnWidth("predecessors", 100);
        this.setColumnHeader("percentageComplete", "% Complete");
        this.setColumnWidth("percentageComplete", 75);
        this.setColumnHeader("assignUser", "Assignee");
        this.setColumnWidth("assignUser", 80);
        this.setColumnCollapsingAllowed(true);
        this.setColumnCollapsed("assignUser", true);
        this.setEditable(true);
        this.setNullSelectionAllowed(false);

        this.addGeneratedColumn("ganttIndex", new ColumnGenerator() {
            @Override
            public Object generateCell(Table table, Object itemId, Object columnId) {
                GanttItemWrapper item = (GanttItemWrapper) itemId;
                return new ELabel("" + item.getGanttIndex()).withStyleName(ValoTheme.LABEL_SMALL);
            }
        });

        this.setTableFieldFactory(new TableFieldFactory() {
            @Override
            public Field<?> createField(Container container, Object itemId, final Object propertyId,
                    Component uiContext) {
                Field field = null;
                final GanttItemWrapper ganttItem = (GanttItemWrapper) itemId;
                if ("name".equals(propertyId)) {
                    field = new AssignmentNameCellField(ganttItem.getType());
                } else if ("percentageComplete".equals(propertyId)) {
                    field = new TextField();
                    ((TextField) field).setNullRepresentation("0");
                    ((TextField) field).setImmediate(true);
                    field.addStyleName(ValoTheme.TEXTFIELD_SMALL);
                    if (ganttItem.hasSubTasks() || ganttItem.isMilestone()) {
                        field.setEnabled(false);
                        ((TextField) field).setDescription("Because this row has sub-tasks, this cell "
                                + "is a summary value and can not be edited directly. You can edit cells "
                                + "beneath this row to change its value");
                    }
                } else if ("startDate".equals(propertyId) || "endDate".equals(propertyId)) {
                    field = new DateField();
                    field.addStyleName(ValoTheme.DATEFIELD_SMALL);
                    ((DateField) field).setConverter(new LocalDateConverter());
                    ((DateField) field).setImmediate(true);
                    if (ganttItem.hasSubTasks()) {
                        field.setEnabled(false);
                        ((DateField) field).setDescription("Because this row has sub-tasks, this cell "
                                + "is a summary value and can not be edited directly. You can edit cells "
                                + "beneath this row to change its value");
                    }
                } else if ("assignUser".equals(propertyId)) {
                    field = new ProjectMemberSelectionField();
                } else if ("predecessors".equals(propertyId)) {
                    field = new DefaultViewField("");
                    ((DefaultViewField) field).setConverter(new PredecessorConverter());
                    return field;
                } else if ("duration".equals(propertyId)) {
                    field = new TextField();
                    ((TextField) field).setConverter(new HumanTimeConverter());
                    field.addStyleName(ValoTheme.TEXTFIELD_SMALL);
                    if (ganttItem.hasSubTasks()) {
                        field.setEnabled(false);
                        ((TextField) field).setDescription("Because this row has sub-tasks, this cell "
                                + "is a summary value and can not be edited directly. You can edit cells "
                                + "beneath this row to change its value");
                    }
                }

                if (field != null) {
                    field.setBuffered(true);
                    field.setWidth("100%");
                    if (ganttItem.isMilestone()) {
                        if (!CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.MILESTONES)) {
                            field.setEnabled(false);
                            ((AbstractComponent) field).setDescription(
                                    AppContext.getMessage(GenericI18Enum.NOTIFICATION_NO_PERMISSION_DO_TASK));
                        }
                    } else if (ganttItem.isTask()) {
                        if (!CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.TASKS)) {
                            field.setEnabled(false);
                            ((AbstractComponent) field).setDescription(
                                    AppContext.getMessage(GenericI18Enum.NOTIFICATION_NO_PERMISSION_DO_TASK));
                        }
                    } else if (ganttItem.isBug()) {
                        if (!CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.BUGS)) {
                            field.setEnabled(false);
                            ((AbstractComponent) field).setDescription(
                                    AppContext.getMessage(GenericI18Enum.NOTIFICATION_NO_PERMISSION_DO_TASK));
                        }
                    } else {
                        throw new MyCollabException(
                                "Do not support gantt item type " + ganttItem.getTask().getType());
                    }

                    if (field instanceof FieldEvents.BlurNotifier) {
                        ((FieldEvents.BlurNotifier) field).addBlurListener(new FieldEvents.BlurListener() {
                            @Override
                            public void blur(FieldEvents.BlurEvent event) {
                                Object o = event.getSource();
                                if (o instanceof Field) {
                                    Field f = (Field) o;
                                    if (f.isModified()) {
                                        f.commit();
                                        EventBusFactory.getInstance().post(new GanttEvent.AddGanttItemUpdateToQueue(
                                                GanttTreeTable.this, ganttItem));
                                        GanttTreeTable.this.refreshRowCache();
                                    }
                                }
                            }
                        });
                    }
                }
                return field;
            }
        });

        this.addExpandListener(new Tree.ExpandListener() {
            @Override
            public void nodeExpand(Tree.ExpandEvent expandEvent) {
                GanttItemWrapper item = (GanttItemWrapper) expandEvent.getItemId();
                List<GanttItemWrapper> subTasks = item.subTasks();
                insertSubSteps(item, subTasks);
            }
        });

        this.addCollapseListener(new Tree.CollapseListener() {
            @Override
            public void nodeCollapse(Tree.CollapseEvent collapseEvent) {
                GanttItemWrapper item = (GanttItemWrapper) collapseEvent.getItemId();
                List<GanttItemWrapper> subTasks = item.subTasks();
                removeSubSteps(item, subTasks);
            }
        });

        this.setCellStyleGenerator(new CellStyleGenerator() {
            @Override
            public String getStyle(Table source, Object itemId, Object propertyId) {
                GanttItemWrapper item = (GanttItemWrapper) itemId;
                if (item.isMilestone()) {
                    return "root";
                } else if (item.isTask()) {
                    return "";
                }
                return "";
            }
        });

        final GanttContextMenu contextMenu = new GanttContextMenu();
        contextMenu.setAsContextMenuOf(this);
        contextMenu.setOpenAutomatically(false);

        ContextMenu.ContextMenuOpenedListener.TableListener tableListener = new ContextMenu.ContextMenuOpenedListener.TableListener() {
            public void onContextMenuOpenFromRow(ContextMenu.ContextMenuOpenedOnTableRowEvent event) {
                GanttItemWrapper item = (GanttItemWrapper) event.getItemId();
                contextMenu.displayContextMenu(item);
                contextMenu.open(GanttTreeTable.this);
            }

            public void onContextMenuOpenFromHeader(ContextMenu.ContextMenuOpenedOnTableHeaderEvent event) {
            }

            public void onContextMenuOpenFromFooter(ContextMenu.ContextMenuOpenedOnTableFooterEvent event) {
            }
        };

        contextMenu.addContextMenuTableListener(tableListener);
        gantt.setVerticalScrollDelegateTarget(this);
        this.setPageLength(currentPageLength);
    }

    GanttItemContainer getRawContainer() {
        return beanContainer;
    }

    @Override
    public void attach() {
        EventBusFactory.getInstance().register(updateTaskInfoHandler);
        super.attach();
    }

    @Override
    public void detach() {
        EventBusFactory.getInstance().unregister(updateTaskInfoHandler);
        super.detach();
    }

    public void loadAssignments() {
        try {
            GanttAssignmentService ganttAssignmentService = ApplicationContextUtil
                    .getSpringBean(GanttAssignmentService.class);
            final List<AssignWithPredecessors> assignments = ganttAssignmentService.getTaskWithPredecessors(
                    Arrays.asList(CurrentProjectVariables.getProjectId()), AppContext.getAccountId());
            if (assignments.size() == 1) {
                ProjectGanttItem projectGanttItem = (ProjectGanttItem) assignments.get(0);
                List<MilestoneGanttItem> milestoneGanttItems = projectGanttItem.getMilestones();
                for (MilestoneGanttItem milestoneGanttItem : milestoneGanttItems) {
                    GanttItemWrapper itemWrapper = new GanttItemWrapper(gantt, milestoneGanttItem);
                    this.addRootAssignments(itemWrapper);
                }

                List<TaskGanttItem> taskGanttItems = projectGanttItem.getTasksWithNoMilestones();
                for (TaskGanttItem taskGanttItem : taskGanttItems) {
                    GanttItemWrapper itemWrapper = new GanttItemWrapper(gantt, taskGanttItem);
                    this.addRootAssignments(itemWrapper);
                }
                this.updateWholeGanttIndexes();
            } else {
                LOG.error("Error to query multiple value " + CurrentProjectVariables.getProjectId());
            }
            isStartedGanttChart = true;
        } catch (Exception e) {
            LOG.error("Error", e);
        }
    }

    private void updateTaskTree(GanttItemWrapper ganttItemWrapper) {
        if (ganttItemWrapper.hasSubTasks()) {
            boolean collapsed = this.isCollapsed(ganttItemWrapper);
            this.setCollapsed(ganttItemWrapper, !collapsed);
            this.setCollapsed(ganttItemWrapper, collapsed);
        }
        this.markAsDirtyRecursive();
    }

    public void addRootAssignments(GanttItemWrapper itemWrapper) {
        int ganttIndex = beanContainer.size() + 1;
        if (itemWrapper.getGanttIndex() == null || ganttIndex != itemWrapper.getGanttIndex()) {
            itemWrapper.setGanttIndex(ganttIndex);
            ganttIndexIsChanged = true;
        }

        beanContainer.addItem(itemWrapper);
        gantt.addTask(itemWrapper);

        if (itemWrapper.hasSubTasks()) {
            beanContainer.setChildrenAllowed(itemWrapper, true);
            this.setCollapsed(itemWrapper, false);
        } else {
            beanContainer.setChildrenAllowed(itemWrapper, false);
        }
    }

    private void insertSubSteps(final GanttItemWrapper parent, final List<GanttItemWrapper> children) {
        final int stepIndex = gantt.getStepIndex(parent.getStep());
        int count = 0;
        if (stepIndex != -1) {
            LocalDate startDate = parent.getStartDate();
            LocalDate endDate = parent.getEndDate();

            for (GanttItemWrapper child : children) {
                if (!beanContainer.containsId(child)) {
                    beanContainer.addItem(child);

                    int ganttIndex = beanContainer.indexOfId(child) + 1;
                    if (child.getGanttIndex() == null
                            || (child.getGanttIndex() != ganttIndex && !isStartedGanttChart)) {
                        child.setGanttIndex(ganttIndex);
                        ganttIndexIsChanged = true;
                    }

                    this.setParent(child, parent);
                    if (!isStartedGanttChart) {
                        gantt.addTask(child);
                    } else {
                        gantt.addTask(stepIndex + count + 1, child);
                    }

                    if (child.hasSubTasks()) {
                        this.setChildrenAllowed(child, true);
                        this.setCollapsed(child, false);
                    } else {
                        this.setChildrenAllowed(child, false);
                    }
                    count++;
                    startDate = DateTimeUtils.min(startDate, child.getStartDate());
                    endDate = DateTimeUtils.max(endDate, child.getEndDate());
                }

            }
            parent.setStartAndEndDate(startDate, endDate, false, false);
            gantt.markStepDirty(parent.getStep());
        }
    }

    void removeSubSteps(final GanttItemWrapper parent, final List<GanttItemWrapper> childs) {
        final int stepIndex = gantt.getStepIndex(parent.getStep());
        if (stepIndex != -1) {
            for (GanttItemWrapper child : childs) {
                if (child.hasSubTasks()) {
                    removeSubSteps(child, child.subTasks());
                }

                gantt.removeStep(child.getStep());
                beanContainer.removeItem(child);
                this.setCollapsed(child, true);
            }
        }
    }

    public void updateWholeGanttIndexes() {
        if (ganttIndexIsChanged) {
            Collection items = beanContainer.getItemIds();
            for (Object item : items) {
                EventBusFactory.getInstance().post(
                        new GanttEvent.AddGanttItemUpdateToQueue(GanttTreeTable.this, (GanttItemWrapper) item));
            }
        }
    }

    private void calculateWholeGanttIndexes() {
        GanttItemWrapper item = beanContainer.firstItemId();
        int index = 1;
        while (item != null) {
            if (item.getGanttIndex() != index) {
                item.setGanttIndex(index);
                ganttIndexIsChanged = true;
            }

            item = beanContainer.nextItemId(item);
            index++;
        }

        if (ganttIndexIsChanged) {
            calculateGanttIndexOfPredecessors();
        }
        this.refreshRowCache();
    }

    private void calculateGanttIndexOfPredecessors() {
        List items = beanContainer.getItemIds();
        for (Object itemId : items) {
            GanttItemWrapper item = (GanttItemWrapper) itemId;
            List<TaskPredecessor> predecessors = item.getPredecessors();
            if (CollectionUtils.isNotEmpty(predecessors)) {
                for (TaskPredecessor predecessor : predecessors) {
                    Integer descId = predecessor.getDescid();
                    String descType = predecessor.getDesctype();
                    GanttItemWrapper predecessorGanttItem = findGanttItem(descId, descType);
                    if (predecessorGanttItem != null) {
                        predecessor.setGanttIndex(predecessorGanttItem.getGanttIndex());
                    }
                }
            }
        }
    }

    private GanttItemWrapper findGanttItem(Integer assignmentId, String assignmentType) {
        List items = beanContainer.getItemIds();
        for (Object itemId : items) {
            GanttItemWrapper item = (GanttItemWrapper) itemId;
            if (assignmentId.intValue() == item.getId().intValue() && assignmentType.equals(item.getType())) {
                return item;
            }
        }
        return null;
    }

    private class GanttContextMenu extends ContextMenu {
        void displayContextMenu(final GanttItemWrapper ganttItemWrapper) {
            this.removeAllItems();
            ContextMenuItem detailMenuItem = this.addItem("Detail", FontAwesome.BARS);
            detailMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                @Override
                public void contextMenuItemClicked(ContextMenuItemClickEvent event) {
                    if (ganttItemWrapper.isTask()) {
                        if (ganttItemWrapper.getId() == null) {
                            //New task, save then go to the task detail view
                            Task newTask = ganttItemWrapper.buildNewTask();
                            ProjectTaskService taskService = ApplicationContextUtil
                                    .getSpringBean(ProjectTaskService.class);
                            taskService.saveWithSession(newTask, AppContext.getUsername());
                            ganttItemWrapper.setId(newTask.getId());
                            EventBusFactory.getInstance()
                                    .post(new TaskEvent.GotoRead(GanttTreeTable.this, newTask.getId()));
                        } else {
                            EventBusFactory.getInstance()
                                    .post(new TaskEvent.GotoRead(GanttTreeTable.this, ganttItemWrapper.getId()));
                        }
                    } else if (ganttItemWrapper.isMilestone()) {
                        EventBusFactory.getInstance()
                                .post(new MilestoneEvent.GotoRead(GanttTreeTable.this, ganttItemWrapper.getId()));
                    } else if (ganttItemWrapper.isBug()) {
                        EventBusFactory.getInstance()
                                .post(new BugEvent.GotoRead(GanttTreeTable.this, ganttItemWrapper.getId()));
                    }
                }
            });

            ContextMenuItem predecessorMenuItem = this.addItem("Predecessors", FontAwesome.MAP_MARKER);
            predecessorMenuItem
                    .setEnabled(CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.TASKS));
            predecessorMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                @Override
                public void contextMenuItemClicked(ContextMenuItemClickEvent contextMenuItemClickEvent) {
                    if (ganttItemWrapper.isTask()) {
                        UI.getCurrent().addWindow(new PredecessorWindow(GanttTreeTable.this, ganttItemWrapper));
                    } else {
                        NotificationUtil.showWarningNotification("Can not edit predecessors for milestone");
                    }
                }
            });

            ContextMenuItem indentMenuItem = this.addItem("Indent", FontAwesome.INDENT);
            indentMenuItem.setEnabled(ganttItemWrapper.isIndentable()
                    && CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.TASKS));
            indentMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                @Override
                public void contextMenuItemClicked(ContextMenuItemClickEvent contextMenuItemClickEvent) {
                    GanttItemWrapper preItemWrapper = beanContainer.prevItemId(ganttItemWrapper);
                    if (preItemWrapper != null && preItemWrapper != ganttItemWrapper.getParent()) {
                        ganttItemWrapper.updateParentRelationship(preItemWrapper);
                        preItemWrapper.calculateDatesByChildTasks();
                        beanContainer.setChildrenAllowed(preItemWrapper, true);
                        beanContainer.setParent(ganttItemWrapper, preItemWrapper);
                        GanttTreeTable.this.setCollapsed(preItemWrapper, false);
                        GanttTreeTable.this.refreshRowCache();
                        EventBusFactory.getInstance().post(
                                new GanttEvent.AddGanttItemUpdateToQueue(GanttTreeTable.this, ganttItemWrapper));
                    }
                }
            });

            ContextMenuItem outdentMenuItem = this.addItem("Outdent", FontAwesome.OUTDENT);
            outdentMenuItem.setEnabled(ganttItemWrapper.isOutdentable()
                    && CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.TASKS));
            outdentMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                @Override
                public void contextMenuItemClicked(ContextMenuItemClickEvent contextMenuItemClickEvent) {
                    GanttItemWrapper parent = ganttItemWrapper.getParent();
                    if (parent != null) {
                        GanttTreeTable.this.setParent(ganttItemWrapper, parent.getParent());
                        ganttItemWrapper.updateParentRelationship(parent.getParent());
                        GanttTreeTable.this.setCollapsed(ganttItemWrapper, false);
                        // Set all below tasks of taskWrapper have parent is taskWrapper
                        GanttItemWrapper nextItem = (GanttItemWrapper) beanContainer.nextItemId(ganttItemWrapper);
                        while (nextItem != null && nextItem.getParent() == parent) {
                            nextItem.updateParentRelationship(ganttItemWrapper);
                            beanContainer.setChildrenAllowed(ganttItemWrapper, true);
                            beanContainer.setParent(nextItem, ganttItemWrapper);
                            EventBusFactory.getInstance()
                                    .post(new GanttEvent.AddGanttItemUpdateToQueue(GanttTreeTable.this, nextItem));
                        }

                        if (ganttItemWrapper.hasSubTasks()) {
                            ganttItemWrapper.calculateDatesByChildTasks();
                        }
                        beanContainer.setChildrenAllowed(ganttItemWrapper, ganttItemWrapper.hasSubTasks());
                        parent.calculateDatesByChildTasks();
                        beanContainer.setChildrenAllowed(parent, parent.hasSubTasks());

                        if (parent.getParent() != null) {
                            parent.getParent().calculateDatesByChildTasks();
                        }
                        GanttTreeTable.this.refreshRowCache();
                        EventBusFactory.getInstance().post(
                                new GanttEvent.AddGanttItemUpdateToQueue(GanttTreeTable.this, ganttItemWrapper));
                    }
                }
            });

            if (beanContainer.indexOfId(ganttItemWrapper) > 0) {
                ContextMenuItem inserRowBeforeMenuItem = this.addItem("Insert row before", FontAwesome.PLUS_CIRCLE);
                inserRowBeforeMenuItem
                        .setEnabled(CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.TASKS));
                inserRowBeforeMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                    @Override
                    public void contextMenuItemClicked(ContextMenuItemClickEvent contextMenuItemClickEvent) {
                        int index = beanContainer.indexOfId(ganttItemWrapper);
                        if (index > 0) {
                            TaskGanttItem newTask = new TaskGanttItem();
                            newTask.setType(ProjectTypeConstants.TASK);
                            newTask.setPrjId(ganttItemWrapper.getTask().getPrjId());
                            newTask.setName("New Task");
                            newTask.setProgress(0d);
                            newTask.setsAccountId(AppContext.getAccountId());
                            GanttItemWrapper newGanttItem = new GanttItemWrapper(gantt, newTask);
                            newGanttItem.setGanttIndex(index + 1);
                            GanttItemWrapper prevItem = beanContainer.prevItemId(ganttItemWrapper);
                            beanContainer.addItemAfter(prevItem, newGanttItem);
                            gantt.addTask(index, newGanttItem);
                            beanContainer.setChildrenAllowed(newGanttItem, newGanttItem.hasSubTasks());

                            if (ganttItemWrapper.getParent() != null) {
                                GanttItemWrapper parentTask = ganttItemWrapper.getParent();
                                beanContainer.setParent(newGanttItem, parentTask);
                                newGanttItem.updateParentRelationship(parentTask);
                                parentTask.calculateDatesByChildTasks();
                            }
                        }

                        calculateWholeGanttIndexes();
                        GanttTreeTable.this.refreshRowCache();
                    }
                });
            }

            ContextMenuItem insertRowAfterMenuItem = this.addItem("Insert row after", FontAwesome.PLUS_CIRCLE);
            insertRowAfterMenuItem
                    .setEnabled(CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.TASKS));
            insertRowAfterMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                @Override
                public void contextMenuItemClicked(ContextMenuItemClickEvent contextMenuItemClickEvent) {
                    int index = beanContainer.indexOfId(ganttItemWrapper) + 1;
                    TaskGanttItem newTask = new TaskGanttItem();
                    newTask.setType(ProjectTypeConstants.TASK);
                    newTask.setPrjId(ganttItemWrapper.getTask().getPrjId());
                    newTask.setName("New Task");
                    newTask.setProgress(0d);
                    newTask.setsAccountId(AppContext.getAccountId());
                    GanttItemWrapper newGanttItem = new GanttItemWrapper(gantt, newTask);
                    newGanttItem.setGanttIndex(index);
                    gantt.addTask(index, newGanttItem);

                    if (ganttItemWrapper.hasSubTasks()) {
                        newGanttItem.updateParentRelationship(ganttItemWrapper);
                        beanContainer.setParent(newGanttItem, ganttItemWrapper);
                        beanContainer.addItemAfter(ganttItemWrapper, newGanttItem);
                        ganttItemWrapper.calculateDatesByChildTasks();
                    } else if (ganttItemWrapper.getParent() != null) {
                        GanttItemWrapper parentTask = ganttItemWrapper.getParent();
                        newGanttItem.updateParentRelationship(parentTask);
                        beanContainer.setParent(newGanttItem, parentTask);
                        beanContainer.addItemAfter(ganttItemWrapper, newGanttItem);
                        parentTask.calculateDatesByChildTasks();
                    }
                    beanContainer.setChildrenAllowed(newGanttItem, newGanttItem.hasSubTasks());
                    calculateWholeGanttIndexes();
                    refreshRowCache();
                }
            });

            ContextMenuItem deleteRowMenuItem = this.addItem("Delete row", FontAwesome.TRASH_O);
            deleteRowMenuItem.setEnabled(CurrentProjectVariables.canAccess(ProjectRolePermissionCollections.TASKS));
            deleteRowMenuItem.addItemClickListener(new ContextMenuItemClickListener() {
                @Override
                public void contextMenuItemClicked(ContextMenuItemClickEvent contextMenuItemClickEvent) {
                    ConfirmDialogExt.show(UI.getCurrent(),
                            AppContext.getMessage(GenericI18Enum.DIALOG_DELETE_TITLE, AppContext.getSiteName()),
                            AppContext.getMessage(GenericI18Enum.DIALOG_DELETE_MULTIPLE_ITEMS_MESSAGE),
                            AppContext.getMessage(GenericI18Enum.BUTTON_YES),
                            AppContext.getMessage(GenericI18Enum.BUTTON_NO), new ConfirmDialog.Listener() {
                                private static final long serialVersionUID = 1L;

                                @Override
                                public void onClose(ConfirmDialog dialog) {
                                    if (dialog.isConfirmed()) {
                                        removeAssignments(ganttItemWrapper);
                                    }
                                }
                            });
                }
            });
        }

        private void removeAssignments(GanttItemWrapper task) {
            EventBusFactory.getInstance()
                    .post(new GanttEvent.DeleteGanttItemUpdateToQueue(GanttTreeTable.this, task));
            gantt.removeStep(task.getStep());
            gantt.markAsDirtyRecursive();

            GanttItemWrapper parentTask = task.getParent();
            if (parentTask != null) {
                parentTask.removeSubTask(task);
                beanContainer.setChildrenAllowed(parentTask, parentTask.hasSubTasks());
            }

            if (task.hasSubTasks()) {
                Iterator<GanttItemWrapper> iter = task.subTasks().iterator();
                while (iter.hasNext()) {
                    GanttItemWrapper subTask = iter.next();
                    iter.remove();
                    removeAssignments(subTask);
                }
            }
            beanContainer.removeItem(task);
            if (parentTask != null) {
                parentTask.calculateDatesByChildTasks();
            }
            calculateWholeGanttIndexes();
        }
    }

    private static class PredecessorConverter implements Converter<String, List> {
        @Override
        public List convertToModel(String value, Class<? extends List> targetType, Locale locale)
                throws ConversionException {
            return null;
        }

        @Override
        public String convertToPresentation(List predecessors, Class<? extends String> targetType, Locale locale)
                throws ConversionException {
            if (CollectionUtils.isNotEmpty(predecessors)) {
                StringBuilder builder = new StringBuilder();
                for (Object item : predecessors) {
                    TaskPredecessor predecessor = (TaskPredecessor) item;
                    if (predecessor.getLagday() == 0) {
                        if (TaskPredecessor.FS.equals(predecessor.getPredestype())) {
                            builder.append(predecessor.getGanttIndex());
                        } else {
                            builder.append(predecessor.getGanttIndex() + predecessor.getPredestype());
                        }
                    } else {
                        builder.append(predecessor.getGanttIndex() + predecessor.getPredestype());
                        if (predecessor.getLagday() > 0) {
                            builder.append("+" + predecessor.getLagday() + "d");
                        } else {
                            builder.append(predecessor.getLagday() + "d");
                        }
                    }

                    builder.append(",");
                }
                if (builder.charAt(builder.length() - 1) == ',') {
                    builder.deleteCharAt(builder.length() - 1);
                }
                return builder.toString();
            } else {
                return "";
            }
        }

        @Override
        public Class<List> getModelType() {
            return List.class;
        }

        @Override
        public Class<String> getPresentationType() {
            return String.class;
        }
    }
}