net.scran24.user.client.survey.foodlist.EditableFoodList.java Source code

Java tutorial

Introduction

Here is the source code for net.scran24.user.client.survey.foodlist.EditableFoodList.java

Source

/*
This file is part of Intake24.
    
 Crown copyright, 2012, 2013, 2014.
    
This software is licensed under the Open Government Licence 3.0:
    
http://www.nationalarchives.gov.uk/doc/open-government-licence/
*/

package net.scran24.user.client.survey.foodlist;

import java.util.ArrayList;
import java.util.List;

import net.scran24.common.client.IEHack;
import net.scran24.common.client.UnorderedList;
import net.scran24.user.shared.FoodEntry;
import net.scran24.user.shared.Meal;
import net.scran24.user.shared.UUID;

import org.pcollections.client.PVector;
import org.workcraft.gwt.shared.client.Callback1;
import org.workcraft.gwt.shared.client.Function1;
import org.workcraft.gwt.shared.client.Option;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;

public class EditableFoodList extends Composite {
    private final UnorderedList<EditableFoodListItem> foodList;
    private final EditableFoodListItem newItem;
    private final boolean markAsDrink;
    private final Callback1<List<FoodEntry>> onChange;

    private boolean indentLinkedFoods = true;

    public EditableFoodList(PVector<FoodEntry> foods, Function1<FoodEntry, Boolean> filter, String addButtonText,
            final boolean markAsDrink, Callback1<List<FoodEntry>> onChange) {
        this(addButtonText, markAsDrink, onChange);

        for (FoodEntry food : foods) {
            if (filter.apply(food) && !food.link.isLinked()) {
                addItem(Option.some(food));

                for (FoodEntry linked : Meal.linkedFoods(foods, food)) {
                    addItem(Option.some(linked));
                }
            }
        }
    }

    private void deselect() {
        for (EditableFoodListItem i : foodList.getItems()) {
            i.highlight(false);
        }
    }

    private void select(EditableFoodListItem item) {
        deselect();
        item.highlight(true);
    }

    private void acceptNewItem() {
        if (newItem.isEmpty())
            return;
        addItem(Option.some(newItem.mkFoodEntry(markAsDrink)));
        newItem.clearText();
        focusNew();
        onChange.call(getEnteredItems());
    }

    public EditableFoodList(String addButtonText, final boolean markAsDrink, Callback1<List<FoodEntry>> onChange) {
        this.markAsDrink = markAsDrink;
        this.onChange = onChange;

        FlowPanel contents = new FlowPanel();
        initWidget(contents);

        foodList = new UnorderedList<EditableFoodListItem>();

        foodList.sinkEvents(Event.ONMOUSEOUT);
        foodList.addHandler(new MouseOutHandler() {

            @Override
            public void onMouseOut(MouseOutEvent event) {
                deselect();
            }
        }, MouseOutEvent.getType());

        newItem = new EditableFoodListItem(Option.<FoodEntry>none());
        newItem.addStyleName("intake24-food-list-new-item");
        newItem.textBox.addStyleName("intake24-food-list-textbox-new-item");

        newItem.deleteButton.removeStyleName("intake24-food-list-delete-button");
        newItem.deleteButton.addStyleName("intake24-food-list-accept-button");
        newItem.deleteButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                acceptNewItem();
            }
        });

        newItem.textBox.addKeyUpHandler(new KeyUpHandler() {
            @Override
            public void onKeyUp(KeyUpEvent event) {
                if (newItem.isEmpty())
                    newItem.deleteButton.getElement().getStyle().setVisibility(Visibility.HIDDEN);
                else
                    newItem.deleteButton.getElement().getStyle().setVisibility(Visibility.VISIBLE);
            }
        });

        UnorderedList<EditableFoodListItem> newItemContainer = new UnorderedList<EditableFoodListItem>();
        newItemContainer.addItem(newItem);

        newItem.showPlaceholderText();

        newItem.textBox.addKeyUpHandler(new KeyUpHandler() {
            @Override
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER && newItem.isChanged()) {
                    acceptNewItem();
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
                    newItem.clearText();
                    newItem.textBox.setFocus(false);
                }
            }
        });

        newItem.textBox.addFocusHandler(new FocusHandler() {
            @Override
            public void onFocus(FocusEvent event) {
                if (newItem.isEmpty())
                    newItem.clearText();
            }
        });

        newItem.textBox.addBlurHandler(new BlurHandler() {
            @Override
            public void onBlur(BlurEvent event) {
                if (newItem.isEmpty())
                    newItem.showPlaceholderText();
            }
        });

        contents.add(foodList);
        contents.add(newItemContainer);
    }

    public List<EditableFoodListItem> linkedItems(EditableFoodListItem item) {
        final ArrayList<EditableFoodListItem> result = new ArrayList<EditableFoodListItem>();

        item.init.accept(new Option.SideEffectVisitor<FoodEntry>() {
            @Override
            public void visitSome(final FoodEntry item) {
                for (final EditableFoodListItem other : foodList.getItems()) {
                    other.init.accept(new Option.SideEffectVisitor<FoodEntry>() {
                        @Override
                        public void visitSome(FoodEntry item2) {
                            item2.link.linkedTo.accept(new Option.SideEffectVisitor<UUID>() {
                                @Override
                                public void visitSome(UUID uuid) {
                                    if (uuid.equals(item.link.id))
                                        result.add(other);
                                }

                                @Override
                                public void visitNone() {
                                }
                            });
                        }

                        @Override
                        public void visitNone() {
                        }
                    });
                }
            }

            @Override
            public void visitNone() {
            }
        });

        return result;
    }

    public void removeItem(EditableFoodListItem item) {
        if (item == newItem)
            return;

        foodList.removeItem(item);

        for (EditableFoodListItem i : linkedItems(item))
            removeItem(i);

        IEHack.forceReflow();
    }

    public EditableFoodListItem addItem(Option<FoodEntry> init) {
        final EditableFoodListItem item = new EditableFoodListItem(init);

        if (!indentLinkedFoods)
            item.removeStyleName("intake24-food-list-linked-item");

        item.textBox.addKeyUpHandler(new KeyUpHandler() {
            @Override
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    item.textBox.setFocus(false);
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
                    item.textBox.setFocus(false);
                }
            }
        });

        item.textBox.addFocusHandler(new FocusHandler() {
            @Override
            public void onFocus(FocusEvent event) {
                select(item);
            }
        });

        item.textBox.addBlurHandler(new BlurHandler() {
            @Override
            public void onBlur(BlurEvent event) {
                if (item.isEmpty()) {
                    removeItem(item);
                    onChange.call(getEnteredItems());
                } else if (item.isChanged()) {
                    onChange.call(getEnteredItems());
                }
            }
        });

        item.sinkEvents(Event.ONMOUSEOVER);

        item.addHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                select(item);
            }
        }, MouseOverEvent.getType());

        item.deleteButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                removeItem(item);
                onChange.call(getEnteredItems());
            }
        });

        foodList.addItem(item);

        IEHack.forceReflowDeferred();

        return item;
    }

    public void disableLinkedFoodsIndentation() {
        indentLinkedFoods = false;
    }

    public List<FoodEntry> getEnteredItems() {
        final ArrayList<FoodEntry> result = new ArrayList<FoodEntry>();
        for (final EditableFoodListItem i : foodList.getItems()) {
            result.add(i.mkFoodEntry(markAsDrink));
        }

        if (!newItem.isEmpty())
            result.add(newItem.mkFoodEntry(markAsDrink));

        return result;
    }

    public void focusNew() {
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            public void execute() {
                // IEHack.forceReflow();
                newItem.textBox.setFocus(true);
            }
        });
    }
}