[Bug #876] Refactor interface for handling operations in a tree (up, down, indent, unindent, etc)

* Now most of the algorithms for these operations can be shared between
OrderElements and Templates.
   * Disable create template button from Templates view.

FEA: ItEr74S04BugFixing
This commit is contained in:
Diego Pino Garcia 2011-04-07 17:06:00 +02:00
parent 74213b1246
commit 0dc540d0a0
8 changed files with 488 additions and 122 deletions

View file

@ -62,7 +62,6 @@ import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Button;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Tab;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Treechildren;
@ -77,6 +76,7 @@ import org.zkoss.zul.impl.api.InputElement;
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
* @author Manuel Rego Casasnovas <mrego@igalia.com>
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
* @author Diego Pino García <dpino@igalia.com>
*/
public class OrderElementTreeController extends TreeController<OrderElement> {
@ -101,6 +101,8 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
@Resource
private IOrderTemplatesControllerEntryPoints orderTemplates;
private OrderElementOperations operationsForOrderElement;
private final IMessagesForUser messagesForUser;
public List<org.navalplanner.business.labels.entities.Label> getLabels() {
@ -119,6 +121,18 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
this.orderModel = orderModel;
this.orderElementController = orderElementController;
this.messagesForUser = messagesForUser;
initializeOperationsForOrderElement();
}
/**
* Initializes operationsForOrderTemplate. A reference to variables tree and
* orderTemplates will be set later in doAfterCompose()
*/
private void initializeOperationsForOrderElement() {
operationsForOrderElement = OrderElementOperations.build()
.treeController(this)
.orderModel(this.orderModel)
.orderElementController(this.orderElementController);
}
public OrderElementController getOrderElementController() {
@ -130,93 +144,36 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
return orderModel.getOrderElementTreeModel();
}
public void createTemplateFromSelectedOrderElement() {
if (tree.getSelectedCount() == 1) {
createTemplate(getSelectedNode());
} else {
showSelectAnElementMessageBox();
}
/**
* Operations for each node
*/
public void editSelectedElement() {
operationsForOrderElement.editSelectedElement();
}
public void editSelectedOrderElement() {
if (tree.getSelectedCount() == 1) {
showEditionOrderElement(tree.getSelectedItem());
} else {
showSelectAnElementMessageBox();
}
public void createTemplateFromSelectedElement() {
operationsForOrderElement.createTemplateFromSelectedElement();
}
public void moveSelectedOrderElementUp() {
if (tree.getSelectedCount() == 1) {
Treeitem item = tree.getSelectedItem();
up((OrderElement)item.getValue());
Treeitem brother = (Treeitem) item.getPreviousSibling();
if (brother != null) {
brother.setSelected(true);
}
} else {
showSelectAnElementMessageBox();
}
public void moveSelectedElementUp() {
operationsForOrderElement.moveSelectedElementUp();
}
public void moveSelectedOrderElementDown() {
if (tree.getSelectedCount() == 1) {
Treeitem item = tree.getSelectedItem();
down((OrderElement)item.getValue());
Treeitem brother = (Treeitem) item.getNextSibling();
if (brother != null) {
brother.setSelected(true);
}
} else {
showSelectAnElementMessageBox();
}
public void moveSelectedElementDown() {
operationsForOrderElement.moveSelectedElementDown();
}
public void indentSelectedOrderElement() {
if (tree.getSelectedCount() == 1) {
indent(getSelectedNode());
} else {
showSelectAnElementMessageBox();
}
public void indentSelectedElement() {
operationsForOrderElement.indentSelectedElement();
}
public void unindentSelectedOrderElement() {
if (tree.getSelectedCount() == 1) {
unindent(getSelectedNode());
} else {
showSelectAnElementMessageBox();
}
public void unindentSelectedElement() {
operationsForOrderElement.unindentSelectedElement();
}
public void deleteSelectedOrderElement() {
if (tree.getSelectedCount() == 1) {
remove(getSelectedNode());
} else {
showSelectAnElementMessageBox();
}
}
private void showSelectAnElementMessageBox() {
try {
Messagebox.show(_("Choose a task "
+ "to operate on it"));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private boolean isTemplateCreationConfirmed() {
try {
int status = Messagebox
.show(
_("Still not saved changes would be lost."
+ " Are you sure you want to go to create a template?"),
"Confirm", Messagebox.YES | Messagebox.NO,
Messagebox.QUESTION);
return Messagebox.YES == status;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
public void deleteSelectedElement() {
operationsForOrderElement.deleteSelectedElement();
}
public void createFromTemplate() {
@ -232,30 +189,6 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
});
}
private void createTemplate(OrderElement selectedNode) {
if (!isTemplateCreationConfirmed()) {
return;
}
if (!selectedNode.isNewObject()) {
orderTemplates.goToCreateTemplateFrom(selectedNode);
} else {
notifyTemplateCantBeCreated();
}
}
private void notifyTemplateCantBeCreated() {
try {
Messagebox
.show(
_("Templates can only be created from already existent tasks.\n"
+ "Newly tasks cannot be used."),
_("Operation cannot be done"), Messagebox.OK,
Messagebox.INFORMATION);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
protected void filterByPredicateIfAny() {
if (predicate != null) {
filterByPredicate();
@ -324,6 +257,8 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
templateFinderPopup = (TemplateFinderPopup) comp
.getFellow("templateFinderPopupAtTree");
operationsForOrderElement.tree(tree)
.orderTemplates(this.orderTemplates);
}
private void appendExpandCollapseButton() {
@ -665,7 +600,7 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
refreshRow(item);
}
private void refreshRow(Treeitem item) {
public void refreshRow(Treeitem item) {
try {
getRenderer().updateHoursFor((OrderElement) item.getValue());
getRenderer().render(item, item.getValue());
@ -725,14 +660,13 @@ public class OrderElementTreeController extends TreeController<OrderElement> {
}
@Override
protected void remove(OrderElement element) {
public void remove(OrderElement element) {
boolean alreadyInUse = orderModel.isAlreadyInUse(element);
if (alreadyInUse) {
messagesForUser
.showMessage(
Level.ERROR,
_(
"You can not remove the task \"{0}\" because of this or any of its children are already in use in some work reports",
_("You can not remove the task \"{0}\" because of this or any of its children are already in use in some work reports",
element.getName()));
} else {
super.remove(element);

View file

@ -0,0 +1,260 @@
/*
* This file is part of NavalPlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* Copyright (C) 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.navalplanner.web.orders;
import static org.navalplanner.web.I18nHelper._;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.web.templates.IOrderTemplatesControllerEntryPoints;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treeitem;
/**
*
* @author Diego Pino García <dpino@igalia.com>
*
* Encapsulates the operations (up, down, indent, unindent, etc) for an
* element of the tree. The element can be an OrderElement or a
* TemplateElement
*/
public abstract class TreeElementOperationsController<T> {
protected Tree tree;
public void editSelectedElement() {
if (tree.getSelectedCount() == 1) {
showEditElement(tree.getSelectedItem());
} else {
showSelectAnElementError();
}
}
protected void showSelectAnElementError() {
try {
Messagebox.show(_("Please select a task"));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
protected abstract void showEditElement(Treeitem treeitem);
public void moveSelectedElementUp() {
if (tree.getSelectedCount() == 1) {
Treeitem item = tree.getSelectedItem();
up((T)item.getValue());
Treeitem brother = (Treeitem) item.getPreviousSibling();
if (brother != null) {
brother.setSelected(true);
}
} else {
showSelectAnElementError();
}
}
protected abstract void up(T element);
public void moveSelectedElementDown() {
if (tree.getSelectedCount() == 1) {
Treeitem item = tree.getSelectedItem();
down((T)item.getValue());
Treeitem brother = (Treeitem) item.getNextSibling();
if (brother != null) {
brother.setSelected(true);
}
} else {
showSelectAnElementError();
}
}
protected abstract void down(T element);
public void indentSelectedElement() {
if (tree.getSelectedCount() == 1) {
indent(getSelectedElement());
} else {
showSelectAnElementError();
}
}
protected abstract T getSelectedElement();
protected abstract void indent(T element);
public void unindentSelectedElement() {
if (tree.getSelectedCount() == 1) {
unindent(getSelectedElement());
} else {
showSelectAnElementError();
}
}
protected abstract void unindent(T element);
public void deleteSelectedElement() {
if (tree.getSelectedCount() == 1) {
remove(getSelectedElement());
} else {
showSelectAnElementError();
}
}
protected abstract void remove(T element);
}
/**
*
* @author Diego Pino García <dpino@igalia.com>
*
* Implements tree operations for an {@link OrderElement}
*
*/
class OrderElementOperations extends TreeElementOperationsController<OrderElement> {
private OrderElementTreeController treeController;
private IOrderModel orderModel;
private OrderElementController orderElementController;
private IOrderTemplatesControllerEntryPoints orderTemplates;
public static OrderElementOperations build() {
return new OrderElementOperations();
}
private OrderElementOperations() {
}
public OrderElementOperations tree(Tree tree) {
super.tree = tree;
return this;
}
public OrderElementOperations treeController(OrderElementTreeController treeController) {
this.treeController = treeController;
return this;
}
public OrderElementOperations orderModel(IOrderModel orderModel) {
this.orderModel = orderModel;
return this;
}
public OrderElementOperations orderElementController(OrderElementController orderElementController) {
this.orderElementController = orderElementController;
return this;
}
public OrderElementOperations orderTemplates(
IOrderTemplatesControllerEntryPoints orderTemplates) {
this.orderTemplates = orderTemplates;
return this;
}
@Override
protected OrderElement getSelectedElement() {
return treeController.getSelectedNode();
}
@Override
protected void up(OrderElement element) {
treeController.up(element);
}
@Override
protected void down(OrderElement element) {
treeController.down(element);
}
@Override
protected void indent(OrderElement element) {
treeController.indent(element);
}
@Override
protected void unindent(OrderElement element) {
treeController.unindent(element);
}
@Override
protected void remove(OrderElement element) {
treeController.remove(element);
}
@Override
protected void showEditElement(Treeitem item) {
OrderElement orderElement = (OrderElement) item.getValue();
treeController.markModifiedTreeitem(item.getTreerow());
IOrderElementModel model = orderModel
.getOrderElementModel(orderElement);
orderElementController.openWindow(model);
treeController.refreshRow(item);
}
public void createTemplateFromSelectedElement() {
if (tree.getSelectedCount() == 1) {
createTemplate(getSelectedElement());
} else {
showSelectAnElementError();
}
}
private void createTemplate(OrderElement element) {
if (element.isNewObject()) {
notifyTemplateCantBeCreated();
return;
}
if (showConfirmCreateTemplateDialog() == Messagebox.OK) {
orderTemplates.goToCreateTemplateFrom(element);
}
}
private int showConfirmCreateTemplateDialog() {
try {
return Messagebox
.show(_("Unsaved changes will be lost. Would you like to continue?",
_("Confirm create template"), Messagebox.YES
| Messagebox.NO, Messagebox.QUESTION));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void notifyTemplateCantBeCreated() {
try {
Messagebox.show(
_("Templates can only be created out of existent tasks."
+ "You are trying to create a template out of a new task.\n"
+ "Please save your project before proceeding."),
_("Operation cannot be done"), Messagebox.OK,
Messagebox.INFORMATION);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,102 @@
/*
* This file is part of NavalPlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* Copyright (C) 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.navalplanner.web.templates;
import org.navalplanner.business.templates.entities.OrderElementTemplate;
import org.navalplanner.web.orders.TreeElementOperationsController;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treeitem;
/**
*
* @author Diego Pino García <dpino@igalia.com>
*
*/
class TemplateElementOperations extends TreeElementOperationsController<OrderElementTemplate> {
private TemplatesTreeController treeController;
private OrderTemplatesController orderTemplatesController;
public static TemplateElementOperations build() {
return new TemplateElementOperations();
}
private TemplateElementOperations() {
}
public TemplateElementOperations tree(Tree tree) {
super.tree = tree;
return this;
}
public TemplateElementOperations treeController(TemplatesTreeController treeController) {
this.treeController = treeController;
return this;
}
public TemplateElementOperations orderTemplatesController(
OrderTemplatesController orderTemplatesController) {
this.orderTemplatesController = orderTemplatesController;
return this;
}
@Override
protected void showEditElement(Treeitem item) {
OrderElementTemplate orderElement = (OrderElementTemplate) item.getValue();
treeController.markModifiedTreeitem(item.getTreerow());
orderTemplatesController.showEditionFor(orderElement);
treeController.refreshRow(item);
}
@Override
protected void up(OrderElementTemplate element) {
treeController.up(element);
}
@Override
protected void down(OrderElementTemplate element) {
treeController.down(element);
}
@Override
protected OrderElementTemplate getSelectedElement() {
return treeController.getSelectedNode();
}
@Override
protected void indent(OrderElementTemplate element) {
treeController.indent(element);
}
@Override
protected void unindent(OrderElementTemplate element) {
treeController.unindent(element);
}
@Override
protected void remove(OrderElementTemplate element) {
treeController.remove(element);
}
}

View file

@ -32,11 +32,13 @@ import org.navalplanner.web.common.Util.Getter;
import org.navalplanner.web.common.Util.Setter;
import org.navalplanner.web.tree.EntitiesTree;
import org.navalplanner.web.tree.TreeController;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Button;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treeitem;
/**
@ -50,6 +52,14 @@ public class TemplatesTreeController extends
private final OrderTemplatesController orderTemplatesController;
private TemplateElementOperations operationsForOrderTemplate;
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
operationsForOrderTemplate.tree(tree);
}
final class TemplatesTreeRenderer extends Renderer {
private final ClassValidator<OrderElementTemplate> validator = new ClassValidator<OrderElementTemplate>(
@ -73,13 +83,17 @@ public class TemplatesTreeController extends
new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
orderTemplatesController
.showEditionFor(currentTemplate);
Treeitem item = getTreeitem(event.getTarget());
operationsForOrderTemplate.showEditElement(item);
}
});
return result;
}
private Treeitem getTreeitem(Component comp) {
return (Treeitem) comp.getParent().getParent().getParent();
}
@Override
protected void addDescriptionCell(final OrderElementTemplate element) {
Textbox textBox = Util.bind(new Textbox(),
@ -177,6 +191,17 @@ public class TemplatesTreeController extends
super(OrderElementTemplate.class);
this.model = model;
this.orderTemplatesController = orderTemplatesController;
initializeOperationsForOrderTemplate();
}
/**
* Initializes operationsForOrderTemplate. A reference to variable tree is
* needed to be added later in doAfterCompose()
*/
private void initializeOperationsForOrderTemplate() {
operationsForOrderTemplate = TemplateElementOperations.build()
.treeController(this)
.orderTemplatesController(this.orderTemplatesController);
}
@Override
@ -251,4 +276,43 @@ public class TemplatesTreeController extends
};
}
}
public void refreshRow(Treeitem item) {
try {
OrderElementTemplate orderElement = (OrderElementTemplate) item
.getValue();
getRenderer().updateHoursFor(orderElement);
getRenderer().render(item, orderElement);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Operations for a node
*/
public void editSelectedElement() {
operationsForOrderTemplate.editSelectedElement();
}
public void moveSelectedElementDown() {
operationsForOrderTemplate.moveSelectedElementDown();
}
public void moveSelectedElementUp() {
operationsForOrderTemplate.moveSelectedElementUp();
}
public void unindentSelectedElement() {
operationsForOrderTemplate.unindentSelectedElement();
}
public void indentSelectedElement() {
operationsForOrderTemplate.indentSelectedElement();
}
public void deleteSelectedElement() {
operationsForOrderTemplate.deleteSelectedElement();
}
}

View file

@ -99,7 +99,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
}
}
protected void indent(T element) {
public void indent(T element) {
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
getModel().indent(element);
filterByPredicateIfAny();
@ -123,7 +123,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
}
}
protected void unindent(T element) {
public void unindent(T element) {
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
getModel().unindent(element);
filterByPredicateIfAny();
@ -154,8 +154,13 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
filterByPredicateIfAny();
}
protected T getSelectedNode() {
return type.cast(tree.getSelectedItemApi().getValue());
public T getSelectedNode() {
Treeitem item = tree.getSelectedItem();
if (item != null) {
Object value = item.getValue();
return value != null ? type.cast(value) : null;
}
return null;
}
public void move(Component dropedIn, Component dragged) {
@ -287,7 +292,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
filterByPredicateIfAny();
}
protected void remove(T element) {
public void remove(T element) {
List<T> parentNodes = getModel().getParents(element);
getModel().removeNode(element);
getRenderer().refreshHoursValueForNodes(parentNodes);
@ -1018,7 +1023,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
protected Set<Treecell> cellsMarkedAsModified = new HashSet<Treecell>();
protected void markModifiedTreeitem(Treerow item) {
public void markModifiedTreeitem(Treerow item) {
Treecell tc = (Treecell) item.getFirstChild();
// Check if marked label has been previously added
if (!(tc.getLastChild() instanceof org.zkoss.zul.Label)) {

View file

@ -59,7 +59,7 @@
</tabs>
<tabpanels>
<tabpanel sclass="orderelements-tab">
<orderElementTree id="orderElementTree"/>
<orderElementTree id="orderElementTree" showCreateTemplateButton="true" />
</tabpanel>
<tabpanel id="tabPanelGeneralData" fulfill="tabGeneralData.onSelect">
<grid fixedLayout="true">

View file

@ -45,31 +45,32 @@
<label value="${i18n:_('Selected node:')}" />
<button id="editOrderElementButton" sclass="icono" image="/common/img/ico_editar1.png"
tooltiptext="${i18n:_('Edit selected task')}"
onClick="treeController.editSelectedOrderElement();" />
onClick="treeController.editSelectedElement();" />
<button id="createTemplateButton" sclass="icono" image="/common/img/ico_derived1.png"
hoverImage="/common/img/ico_derived.png"
tooltiptext="${i18n:_('Create template from selected task')}"
onClick="treeController.createTemplateFromSelectedOrderElement();" />
onClick="treeController.createTemplateFromSelectedElement();"
if="${arg.showCreateTemplateButton}"/>
<button id="downButton" sclass="icono" image="/common/img/ico_bajar1.png"
hoverImage="/common/img/ico_bajar.png"
tooltiptext="${i18n:_('Move selected task down')}"
onClick="treeController.moveSelectedOrderElementDown();" />
onClick="treeController.moveSelectedElementDown();" />
<button id="upButton" sclass="icono" image="/common/img/ico_subir1.png"
hoverImage="/common/img/ico_subir.png"
tooltiptext="${i18n:_('Move selected task up')}"
onClick="treeController.moveSelectedOrderElementUp();" />
onClick="treeController.moveSelectedElementUp();" />
<button id="leftButton" sclass="icono" image="/common/img/ico_izq1.png"
hoverImage="/common/img/ico_izq.png"
tooltiptext="${i18n:_('Unindent selected task')}"
onClick="treeController.unindentSelectedOrderElement();" />
onClick="treeController.unindentSelectedElement();" />
<button id="rightButton" sclass="icono" image="/common/img/ico_derecha1.png"
hoverImage="/common/img/ico_derecha.png"
tooltiptext="${i18n:_('Indent selected task')}"
onClick="treeController.indentSelectedOrderElement();" />
onClick="treeController.indentSelectedElement();" />
<button id="deleteOrderElementButton" sclass="icono" image="/common/img/ico_borrar1.png"
hoverImage="/common/img/ico_borrar.png"
tooltiptext="${i18n:_('Delete selected task')}"
onClick="treeController.deleteSelectedOrderElement();" />
onClick="treeController.deleteSelectedElement();" />
</hbox></hbox>
<vbox width="100%">
<tree id="tree" height="440px" multiple="false" droppable="true"

View file

@ -101,7 +101,7 @@
</grid>
</tabpanel>
<tabpanel>
<orderElementTree id="orderElementTree"/>
<orderElementTree id="orderElementTree" showCreateTemplateButton="false" />
</tabpanel>
<tabpanel>
<advancesAssignment id="advancesAssignment" />