1d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardpackage autotest.common.ui;
2d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
3d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.ChangeEvent;
4d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.ChangeHandler;
5d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.ClickEvent;
6d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.ClickHandler;
7d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.DoubleClickEvent;
8d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.DoubleClickHandler;
9d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.dom.client.HasClickHandlers;
10d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.shared.GwtEvent;
11d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport com.google.gwt.event.shared.HandlerRegistration;
12d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
13d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport java.util.ArrayList;
14d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport java.util.Collections;
15d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport java.util.HashSet;
16d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport java.util.List;
17d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardimport java.util.Set;
18d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
19d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
20d9e04c1a7950691cc348e70fa2470f8c414ae94fshowardpublic class MultiListSelectPresenter implements ClickHandler, DoubleClickHandler, ChangeHandler {
21d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    /* Simple display showing two list boxes, one of available items and one of selected items */
22d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public interface DoubleListDisplay {
23d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HasClickHandlers getAddAllButton();
24d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HasClickHandlers getAddButton();
25d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HasClickHandlers getRemoveButton();
26d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HasClickHandlers getRemoveAllButton();
27d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HasClickHandlers getMoveUpButton();
28d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HasClickHandlers getMoveDownButton();
29d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public SimplifiedList getAvailableList();
30d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public SimplifiedList getSelectedList();
31d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        // ListBoxes don't support DoubleClickEvents themselves, so the display needs to handle them
32d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler);
33d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
34d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
356f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li    /* Optional additional display allowing toggle between a simple ListBox and a
366f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li     * DoubleListSelector
37d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward     */
38d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public interface ToggleDisplay {
39d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public SimplifiedList getSingleSelector();
40d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public ToggleControl getToggleMultipleLink();
41d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public void setDoubleListVisible(boolean doubleListVisible);
42d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
43d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
44d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public interface GeneratorHandler {
45d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        /**
46d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward         * The given generated Item was just deselected; handle any necessary cleanup.
47d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward         */
48d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public void onRemoveGeneratedItem(Item generatedItem);
49d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
50d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
51d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public static class Item implements Comparable<Item> {
52d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public String name;
53d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public String value;
54d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        // a generated item is destroyed when deselected.
55d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public boolean isGeneratedItem;
56d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
57d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        private boolean selected;
58d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
59d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        private Item(String name, String value) {
60d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            this.name = name;
61d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            this.value = value;
62d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
63d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
64d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public static Item createItem(String name, String value) {
65d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return new Item(name, value);
66d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
67d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
68d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public static Item createGeneratedItem(String name, String value) {
69d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            Item item = new Item(name, value);
70d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            item.isGeneratedItem = true;
71d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return item;
72d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
73d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
74d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public int compareTo(Item item) {
75d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return name.compareTo(item.name);
76d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
77d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
78d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        @Override
79d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public boolean equals(Object obj) {
80d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (!(obj instanceof Item)) {
81d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                return false;
82d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
83d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            Item other = (Item) obj;
84d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return name.equals(other.name);
85d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
86d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
87d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        @Override
88d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public int hashCode() {
89d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return name.hashCode();
90d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
91d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
92d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        @Override
93d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public String toString() {
94d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return "Item<" + name + ", " + value + ">";
95d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
966f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
978b0ea2285c1327a686ff0b6ab245915e7fd20094showard        private boolean isSelected() {
98d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (isGeneratedItem) {
99d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                return true;
100d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
101d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return selected;
102d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
1036f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
1048b0ea2285c1327a686ff0b6ab245915e7fd20094showard        private void setSelected(boolean selected) {
1058b0ea2285c1327a686ff0b6ab245915e7fd20094showard            assert !isGeneratedItem;
106d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            this.selected = selected;
107d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
108d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
109d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
1108b0ea2285c1327a686ff0b6ab245915e7fd20094showard    /**
1118b0ea2285c1327a686ff0b6ab245915e7fd20094showard     * Null object to support displays that don't do toggling.
1128b0ea2285c1327a686ff0b6ab245915e7fd20094showard     */
113d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private static class NullToggleDisplay implements ToggleDisplay {
114d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        @Override
115d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public SimplifiedList getSingleSelector() {
116d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return new SimplifiedList() {
117d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
118d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public void addItem(String name, String value) {
119d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    return;
120d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
121d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
122d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
123d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public void clear() {
124d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    return;
125d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
126d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
127d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
128d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public String getSelectedName() {
129d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    return "";
130d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
131d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
132d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
133d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public void selectByName(String name) {
134d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    return;
135d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
136d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
137d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
138d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public HandlerRegistration addChangeHandler(ChangeHandler handler) {
139d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    throw new UnsupportedOperationException();
140d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
1416f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
1426f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li                @Override
1436f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li                public void setEnabled(boolean enabled) {
1446f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li                    throw new UnsupportedOperationException();
1456f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li                }
146d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            };
147d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
148d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
149d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        @Override
150d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public ToggleControl getToggleMultipleLink() {
151d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return new ToggleControl() {
152d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
153d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public HandlerRegistration addClickHandler(ClickHandler handler) {
154d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    throw new UnsupportedOperationException();
155d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
156d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
157d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
158d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public void fireEvent(GwtEvent<?> event) {
159d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    throw new UnsupportedOperationException();
160d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
161d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
162d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
163d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public boolean isActive() {
164d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    return true;
165d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
166d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
167d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                @Override
168d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                public void setActive(boolean active) {
169d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                    return;
170d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                }
171d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            };
172d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
173d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
174d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        @Override
175d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        public void setDoubleListVisible(boolean doubleListVisible) {
176d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return;
177d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
178d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
179d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
180d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private List<Item> items = new ArrayList<Item>();
181d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    // need a second list to track ordering
182d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private List<Item> selectedItems = new ArrayList<Item>();
183d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private DoubleListDisplay display;
184d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private ToggleDisplay toggleDisplay = new NullToggleDisplay();
185d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private GeneratorHandler generatorHandler;
186d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
187d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void setGeneratorHandler(GeneratorHandler handler) {
188d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        this.generatorHandler = handler;
189d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
190d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
191d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void bindDisplay(DoubleListDisplay display) {
192d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        this.display = display;
193d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getAddAllButton().addClickHandler(this);
194d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getAddButton().addClickHandler(this);
195d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getRemoveButton().addClickHandler(this);
196d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getRemoveAllButton().addClickHandler(this);
197d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getMoveUpButton().addClickHandler(this);
198d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getMoveDownButton().addClickHandler(this);
199d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.addDoubleClickHandler(this);
200d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
201d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
202d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void bindToggleDisplay(ToggleDisplay toggleDisplay) {
203d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        this.toggleDisplay = toggleDisplay;
204d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.getSingleSelector().addChangeHandler(this);
205d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.getToggleMultipleLink().addClickHandler(this);
206d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.getToggleMultipleLink().setActive(false);
207d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
208d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
209d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private boolean verifyConsistency() {
210d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        // check consistency of selectedItems
211d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : items) {
212d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (item.isSelected() && !selectedItems.contains(item)) {
2136f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li                throw new RuntimeException("selectedItems is inconsistent, missing: "
214d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                                           + item.toString());
215d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
216d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
217d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        return true;
218d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
219d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
220d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void addItem(Item item) {
2218b0ea2285c1327a686ff0b6ab245915e7fd20094showard        if (item.isGeneratedItem && isItemPresent(item)) {
22216ab5250da1e2e5fb1549774063a8099ce4fdbb8showard            return;
223d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
224d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        items.add(item);
225d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        Collections.sort(items);
226d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (item.isSelected()) {
227d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            selectedItems.add(item);
228d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
229d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert verifyConsistency();
230d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
231d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
23216ab5250da1e2e5fb1549774063a8099ce4fdbb8showard
23316ab5250da1e2e5fb1549774063a8099ce4fdbb8showard    private boolean isItemPresent(Item item) {
23416ab5250da1e2e5fb1549774063a8099ce4fdbb8showard        return Collections.binarySearch(items, item) >= 0;
23516ab5250da1e2e5fb1549774063a8099ce4fdbb8showard    }
2366f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
237d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void removeItem(Item item) {
238d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        items.remove(item);
239d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (item.isSelected()) {
240d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            selectedItems.remove(item);
241d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
242d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert verifyConsistency();
243d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
244d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
245d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
2468b0ea2285c1327a686ff0b6ab245915e7fd20094showard    public void clearItems() {
2478b0ea2285c1327a686ff0b6ab245915e7fd20094showard        for (Item item : new ArrayList<Item>(items)) {
2488b0ea2285c1327a686ff0b6ab245915e7fd20094showard            removeItem(item);
2498b0ea2285c1327a686ff0b6ab245915e7fd20094showard        }
250d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
251d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
252d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void refreshSingleSelector() {
253d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        SimplifiedList selector = toggleDisplay.getSingleSelector();
254d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
255d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (!selectedItems.isEmpty()) {
256d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            assert selectedItems.size() == 1;
257d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
258d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
259d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        selector.clear();
260d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : items) {
261d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            selector.addItem(item.name, item.value);
262d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (item.isSelected()) {
263d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                selector.selectByName(item.name);
264d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
265d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
266d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
267d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
268d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void refreshMultipleSelector() {
269d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getAvailableList().clear();
270d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : items) {
271d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (!item.isSelected()) {
272d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                display.getAvailableList().addItem(item.name, item.value);
273d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
274d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
275d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
276d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getSelectedList().clear();
277d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : selectedItems) {
278d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            display.getSelectedList().addItem(item.name, item.value);
279d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
280d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
281d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
282d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void refresh() {
283d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (selectedItems.size() > 1) {
284d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            switchToMultiple();
285d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
286d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (isMultipleSelectActive()) {
287d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            refreshMultipleSelector();
288d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else {
289d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            // single selector always needs something selected
2908b0ea2285c1327a686ff0b6ab245915e7fd20094showard            if (selectedItems.size() == 0 && !items.isEmpty()) {
2918b0ea2285c1327a686ff0b6ab245915e7fd20094showard                selectItem(items.get(0));
292d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
293d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            refreshSingleSelector();
294a29e963afc9de0ab086b5b2bea2e43ff68d81038showard        }
295d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
296d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
297d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void selectItem(Item item) {
2988b0ea2285c1327a686ff0b6ab245915e7fd20094showard        item.setSelected(true);
2998b0ea2285c1327a686ff0b6ab245915e7fd20094showard        selectedItems.add(item);
300d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert verifyConsistency();
301d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
3026f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
303d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void selectItemByName(String name) {
304d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        selectItem(getItemByName(name));
305d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
306d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
3078b0ea2285c1327a686ff0b6ab245915e7fd20094showard
3088b0ea2285c1327a686ff0b6ab245915e7fd20094showard    /**
3098b0ea2285c1327a686ff0b6ab245915e7fd20094showard     * Set the set of selected items by specifying item names.  All names must exist in the set of
3108b0ea2285c1327a686ff0b6ab245915e7fd20094showard     * header fields.
3118b0ea2285c1327a686ff0b6ab245915e7fd20094showard     */
312d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void setSelectedItemsByName(List<String> names) {
313d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (String itemName : names) {
314d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            Item item = getItemByName(itemName);
315d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (!item.isSelected()) {
316d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                selectItem(item);
317d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
318d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
319a29e963afc9de0ab086b5b2bea2e43ff68d81038showard
320d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        Set<String> selectedNames = new HashSet<String>(names);
321a29e963afc9de0ab086b5b2bea2e43ff68d81038showard        for (Item item : getItemsCopy()) {
322d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (item.isSelected() && !selectedNames.contains(item.name)) {
323d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                deselectItem(item);
324d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
325d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
326d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
327d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (selectedItems.size() < 2) {
328d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            switchToSingle();
329d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
330d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
331d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
332d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
3338b0ea2285c1327a686ff0b6ab245915e7fd20094showard    /**
3346f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li     * Set the set of selected items, silently dropping any that don't exist in the header field
3358b0ea2285c1327a686ff0b6ab245915e7fd20094showard     * list.
3368b0ea2285c1327a686ff0b6ab245915e7fd20094showard     */
3378b0ea2285c1327a686ff0b6ab245915e7fd20094showard    public void restoreSelectedItems(List<Item> items) {
3388b0ea2285c1327a686ff0b6ab245915e7fd20094showard        List<String> currentItems = new ArrayList<String>();
3398b0ea2285c1327a686ff0b6ab245915e7fd20094showard        for (Item item : items) {
3408b0ea2285c1327a686ff0b6ab245915e7fd20094showard            if (hasItemName(item.name)) {
3418b0ea2285c1327a686ff0b6ab245915e7fd20094showard                currentItems.add(item.name);
3428b0ea2285c1327a686ff0b6ab245915e7fd20094showard            }
3438b0ea2285c1327a686ff0b6ab245915e7fd20094showard        }
3448b0ea2285c1327a686ff0b6ab245915e7fd20094showard        setSelectedItemsByName(currentItems);
3458b0ea2285c1327a686ff0b6ab245915e7fd20094showard    }
3468b0ea2285c1327a686ff0b6ab245915e7fd20094showard
347d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void deselectItem(Item item) {
348d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (item.isGeneratedItem) {
349d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            removeItem(item);
3508b0ea2285c1327a686ff0b6ab245915e7fd20094showard            generatorHandler.onRemoveGeneratedItem(item);
351d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else {
352d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            item.setSelected(false);
353d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            selectedItems.remove(item);
354d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
355d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert verifyConsistency();
356d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
357d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
358d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public List<Item> getSelectedItems() {
3598b0ea2285c1327a686ff0b6ab245915e7fd20094showard        return new ArrayList<Item>(selectedItems);
360d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
361d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
362d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private boolean isMultipleSelectActive() {
363d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        return toggleDisplay.getToggleMultipleLink().isActive();
364d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
365d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
366d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void switchToSingle() {
367d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        // reduce selection to the first selected item
368d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        while (selectedItems.size() > 1) {
369d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            deselectItem(selectedItems.get(1));
370d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
371d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
372d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.setDoubleListVisible(false);
373d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.getToggleMultipleLink().setActive(false);
374d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
375d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
376d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void switchToMultiple() {
377d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.setDoubleListVisible(true);
378d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        toggleDisplay.getToggleMultipleLink().setActive(true);
379d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
380d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
381d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private Item getItemByName(String name) {
3828b0ea2285c1327a686ff0b6ab245915e7fd20094showard        Item item = findItem(name);
3838b0ea2285c1327a686ff0b6ab245915e7fd20094showard        if (item != null) {
3848b0ea2285c1327a686ff0b6ab245915e7fd20094showard            return item;
3858b0ea2285c1327a686ff0b6ab245915e7fd20094showard        }
3868b0ea2285c1327a686ff0b6ab245915e7fd20094showard        throw new IllegalArgumentException("Item '" + name + "' does not exist in " + items);
3878b0ea2285c1327a686ff0b6ab245915e7fd20094showard    }
3888b0ea2285c1327a686ff0b6ab245915e7fd20094showard
3898b0ea2285c1327a686ff0b6ab245915e7fd20094showard    private Item findItem(String name) {
390d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : items) {
391d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (item.name.equals(name)) {
392d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                return item;
393d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
394d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
3958b0ea2285c1327a686ff0b6ab245915e7fd20094showard        return null;
3968b0ea2285c1327a686ff0b6ab245915e7fd20094showard    }
3976f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
3988b0ea2285c1327a686ff0b6ab245915e7fd20094showard    public boolean hasItemName(String name) {
3998b0ea2285c1327a686ff0b6ab245915e7fd20094showard        return findItem(name) != null;
400d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
401d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
402d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    @Override
403d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void onClick(ClickEvent event) {
4046f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li        boolean isItemSelectedOnLeft = display.getAvailableList().getSelectedName() != null;
4056f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li        boolean isItemSelectedOnRight = display.getSelectedList().getSelectedName() != null;
406d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        Object source = event.getSource();
407d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (source == display.getAddAllButton()) {
408d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            addAll();
409d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else if (source == display.getAddButton() && isItemSelectedOnLeft) {
410d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            doSelect();
411d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else if (source == display.getRemoveButton() && isItemSelectedOnRight) {
412d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            doDeselect();
413d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else if (source == display.getRemoveAllButton()) {
414227a7a1d0484dcfa4c6d996a1c10e95437d059efshoward            deselectAll();
415d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else if ((source == display.getMoveUpButton() || source == display.getMoveDownButton())
416d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                   && isItemSelectedOnRight) {
417d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            reorderItem(source == display.getMoveUpButton());
418d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return; // don't refresh again or we'll mess up the user's selection
419d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else if (source == toggleDisplay.getToggleMultipleLink()) {
420d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (toggleDisplay.getToggleMultipleLink().isActive()) {
421d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                switchToMultiple();
422d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            } else {
423d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                switchToSingle();
424d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
425d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else {
426d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            throw new RuntimeException("Unexpected ClickEvent from " + event.getSource());
427d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
4286f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
429d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
430d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
431d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
432d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    @Override
433d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void onDoubleClick(DoubleClickEvent event) {
434d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        Object source = event.getSource();
435d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        if (source == display.getAvailableList()) {
436d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            doSelect();
437d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else if (source == display.getSelectedList()) {
438d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            doDeselect();
439d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        } else {
440d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            // ignore double-clicks on other widgets
441d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            return;
442d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
4436f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
444d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
445d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
446d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
447d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    @Override
448d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    public void onChange(ChangeEvent event) {
449d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert toggleDisplay != null;
450d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        SimplifiedList selector = toggleDisplay.getSingleSelector();
451d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert event.getSource() == selector;
452d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        // events should only come from the single selector when it's active
453d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        assert !toggleDisplay.getToggleMultipleLink().isActive();
454a29e963afc9de0ab086b5b2bea2e43ff68d81038showard
455d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : getItemsCopy()) {
456d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (item.isSelected()) {
457d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                deselectItem(item);
458d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            } else if (item.name.equals(selector.getSelectedName())) {
459d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                selectItem(item);
460d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
461d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
4626f27d4f22a1ba5063968b8c322fa0845f3279adeEric Li
463d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
464d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
465d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
466d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    /**
467d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward     * Selecting or deselecting items can add or remove items (due to generators), so sometimes we
468d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward     * need to iterate over a copy.
469d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward     */
470d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private Iterable<Item> getItemsCopy() {
471d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        return new ArrayList<Item>(items);
472d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
473d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
474d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void doSelect() {
475d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        selectItem(getItemByName(display.getAvailableList().getSelectedName()));
476d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
477d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
478d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void doDeselect() {
479d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        deselectItem(getItemByName(display.getSelectedList().getSelectedName()));
480d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
481d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
482d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void addAll() {
483d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : items) {
4848b0ea2285c1327a686ff0b6ab245915e7fd20094showard            if (!item.isSelected()) {
485d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                selectItem(item);
486d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
487d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
488d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
489d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
490227a7a1d0484dcfa4c6d996a1c10e95437d059efshoward    public void deselectAll() {
491d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        for (Item item : getItemsCopy()) {
492d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            if (item.isSelected()) {
493d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward                deselectItem(item);
494d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward            }
495d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        }
496d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
497d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward
498d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    private void reorderItem(boolean moveUp) {
499d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        Item item = getItemByName(display.getSelectedList().getSelectedName());
500d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        int positionDelta = moveUp ? -1 : 1;
501d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        int newPosition = selectedItems.indexOf(item) + positionDelta;
502d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        newPosition = Math.max(0, Math.min(selectedItems.size() - 1, newPosition));
503d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        selectedItems.remove(item);
504d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        selectedItems.add(newPosition, item);
505d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        refresh();
506d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward        display.getSelectedList().selectByName(item.name);
507d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward    }
508d9e04c1a7950691cc348e70fa2470f8c414ae94fshoward}
509