You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VTwinColSelect.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.HashSet;
  20. import java.util.List;
  21. import java.util.Objects;
  22. import java.util.Set;
  23. import java.util.function.BiConsumer;
  24. import java.util.stream.Collectors;
  25. import com.google.gwt.dom.client.Style.Overflow;
  26. import com.google.gwt.dom.client.Style.Position;
  27. import com.google.gwt.event.dom.client.ClickEvent;
  28. import com.google.gwt.event.dom.client.ClickHandler;
  29. import com.google.gwt.event.dom.client.DoubleClickEvent;
  30. import com.google.gwt.event.dom.client.DoubleClickHandler;
  31. import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
  32. import com.google.gwt.event.dom.client.KeyCodes;
  33. import com.google.gwt.event.dom.client.KeyDownEvent;
  34. import com.google.gwt.event.dom.client.KeyDownHandler;
  35. import com.google.gwt.event.dom.client.MouseDownEvent;
  36. import com.google.gwt.event.dom.client.MouseDownHandler;
  37. import com.google.gwt.event.shared.HandlerRegistration;
  38. import com.google.gwt.user.client.ui.Composite;
  39. import com.google.gwt.user.client.ui.FlowPanel;
  40. import com.google.gwt.user.client.ui.HTML;
  41. import com.google.gwt.user.client.ui.HasEnabled;
  42. import com.google.gwt.user.client.ui.ListBox;
  43. import com.google.gwt.user.client.ui.Panel;
  44. import com.google.gwt.user.client.ui.Widget;
  45. import com.vaadin.client.Focusable;
  46. import com.vaadin.client.StyleConstants;
  47. import com.vaadin.client.WidgetUtil;
  48. import com.vaadin.client.connectors.AbstractMultiSelectConnector.MultiSelectWidget;
  49. import com.vaadin.shared.Registration;
  50. import elemental.json.JsonObject;
  51. /**
  52. * A list builder widget that has two selects; one for selectable options,
  53. * another for selected options, and buttons for selecting and deselecting the
  54. * items.
  55. *
  56. * @author Vaadin Ltd
  57. */
  58. public class VTwinColSelect extends Composite implements MultiSelectWidget,
  59. Field, ClickHandler, Focusable, HasEnabled, KeyDownHandler,
  60. MouseDownHandler, DoubleClickHandler, SubPartAware {
  61. private static final String SUBPART_OPTION_SELECT = "leftSelect";
  62. private static final String SUBPART_OPTION_SELECT_ITEM = SUBPART_OPTION_SELECT
  63. + "-item";
  64. private static final String SUBPART_SELECTION_SELECT = "rightSelect";
  65. private static final String SUBPART_SELECTION_SELECT_ITEM = SUBPART_SELECTION_SELECT
  66. + "-item";
  67. private static final String SUBPART_LEFT_CAPTION = "leftCaption";
  68. private static final String SUBPART_RIGHT_CAPTION = "rightCaption";
  69. private static final String SUBPART_ADD_BUTTON = "add";
  70. private static final String SUBPART_REMOVE_BUTTON = "remove";
  71. /** Primary style name for twin col select. */
  72. public static final String CLASSNAME = "v-select-twincol";
  73. private static final int VISIBLE_COUNT = 10;
  74. private static final int DEFAULT_COLUMN_COUNT = 10;
  75. private final DoubleClickListBox optionsListBox;
  76. private final DoubleClickListBox selectionsListBox;
  77. private final FlowPanel optionsContainer;
  78. private final FlowPanel captionWrapper;
  79. private final VButton addItemsLeftToRightButton;
  80. private final VButton removeItemsRightToLeftButton;
  81. private final FlowPanel buttons;
  82. private final Panel panel;
  83. private HTML optionsCaption = null;
  84. private HTML selectionsCaption = null;
  85. private List<BiConsumer<Set<String>, Set<String>>> selectionChangeListeners;
  86. private boolean enabled;
  87. private boolean readOnly;
  88. private int rows = 0;
  89. /**
  90. * A multiselect ListBox which catches double clicks.
  91. */
  92. public class DoubleClickListBox extends ListBox
  93. implements HasDoubleClickHandlers {
  94. /**
  95. * Constructs a new DoubleClickListBox.
  96. */
  97. public DoubleClickListBox() {
  98. setMultipleSelect(true);
  99. }
  100. @Override
  101. public HandlerRegistration addDoubleClickHandler(
  102. DoubleClickHandler handler) {
  103. return addDomHandler(handler, DoubleClickEvent.getType());
  104. }
  105. }
  106. /**
  107. * Constructs a new VTwinColSelect.
  108. */
  109. public VTwinColSelect() {
  110. selectionChangeListeners = new ArrayList<>();
  111. optionsContainer = new FlowPanel();
  112. initWidget(optionsContainer);
  113. optionsContainer.setStyleName(CLASSNAME);
  114. captionWrapper = new FlowPanel();
  115. optionsListBox = new DoubleClickListBox();
  116. optionsListBox.addClickHandler(this);
  117. optionsListBox.addDoubleClickHandler(this);
  118. optionsListBox.setVisibleItemCount(VISIBLE_COUNT);
  119. optionsListBox.setStyleName(CLASSNAME + "-options");
  120. selectionsListBox = new DoubleClickListBox();
  121. selectionsListBox.addClickHandler(this);
  122. selectionsListBox.addDoubleClickHandler(this);
  123. selectionsListBox.setVisibleItemCount(VISIBLE_COUNT);
  124. selectionsListBox.setStyleName(CLASSNAME + "-selections");
  125. buttons = new FlowPanel();
  126. buttons.setStyleName(CLASSNAME + "-buttons");
  127. addItemsLeftToRightButton = new VButton();
  128. addItemsLeftToRightButton.setText(">>");
  129. addItemsLeftToRightButton.addClickHandler(this);
  130. removeItemsRightToLeftButton = new VButton();
  131. removeItemsRightToLeftButton.setText("<<");
  132. removeItemsRightToLeftButton.addClickHandler(this);
  133. panel = optionsContainer;
  134. panel.add(captionWrapper);
  135. captionWrapper.getElement().getStyle().setOverflow(Overflow.HIDDEN);
  136. // Hide until there actually is a caption to prevent IE from rendering
  137. // extra empty space
  138. captionWrapper.setVisible(false);
  139. panel.add(optionsListBox);
  140. buttons.add(addItemsLeftToRightButton);
  141. final HTML br = new HTML("<span/>");
  142. br.setStyleName(CLASSNAME + "-deco");
  143. buttons.add(br);
  144. buttons.add(removeItemsRightToLeftButton);
  145. panel.add(buttons);
  146. panel.add(selectionsListBox);
  147. optionsListBox.addKeyDownHandler(this);
  148. optionsListBox.addMouseDownHandler(this);
  149. selectionsListBox.addMouseDownHandler(this);
  150. selectionsListBox.addKeyDownHandler(this);
  151. updateEnabledState();
  152. }
  153. /**
  154. * Gets the options caption HTML Widget.
  155. *
  156. * @return the options caption widget
  157. */
  158. protected HTML getOptionsCaption() {
  159. if (optionsCaption == null) {
  160. optionsCaption = new HTML();
  161. optionsCaption.setStyleName(CLASSNAME + "-caption-left");
  162. optionsCaption.getElement().getStyle()
  163. .setFloat(com.google.gwt.dom.client.Style.Float.LEFT);
  164. captionWrapper.add(optionsCaption);
  165. }
  166. return optionsCaption;
  167. }
  168. /**
  169. * Gets the selections caption HTML widget.
  170. *
  171. * @return the selections caption widget
  172. */
  173. protected HTML getSelectionsCaption() {
  174. if (selectionsCaption == null) {
  175. selectionsCaption = new HTML();
  176. selectionsCaption.setStyleName(CLASSNAME + "-caption-right");
  177. selectionsCaption.getElement().getStyle()
  178. .setFloat(com.google.gwt.dom.client.Style.Float.RIGHT);
  179. captionWrapper.add(selectionsCaption);
  180. }
  181. return selectionsCaption;
  182. }
  183. /**
  184. * For internal use only. May be removed or replaced in the future.
  185. *
  186. * @return the caption wrapper widget
  187. */
  188. public Widget getCaptionWrapper() {
  189. return captionWrapper;
  190. }
  191. /**
  192. * Sets the number of visible items for the list boxes.
  193. *
  194. * @param rows
  195. * the number of items to show
  196. * @see ListBox#setVisibleItemCount(int)
  197. */
  198. public void setRows(int rows) {
  199. if (this.rows != rows) {
  200. this.rows = rows;
  201. optionsListBox.setVisibleItemCount(rows);
  202. selectionsListBox.setVisibleItemCount(rows);
  203. }
  204. }
  205. /**
  206. * Returns the number of visible items for the list boxes.
  207. *
  208. * @return the number of items to show
  209. * @see ListBox#setVisibleItemCount(int)
  210. */
  211. public int getRows() {
  212. return rows;
  213. }
  214. /**
  215. * Updates the captions above the left (options) and right (selections)
  216. * columns. {code null} value clear the caption.
  217. *
  218. * @param leftCaption
  219. * the left caption to set, or {@code null} to clear
  220. * @param rightCaption
  221. * the right caption to set, or {@code null} to clear
  222. */
  223. public void updateCaptions(String leftCaption, String rightCaption) {
  224. boolean hasCaptions = leftCaption != null || rightCaption != null;
  225. if (leftCaption == null) {
  226. removeOptionsCaption();
  227. } else {
  228. getOptionsCaption().setText(leftCaption);
  229. }
  230. if (rightCaption == null) {
  231. removeSelectionsCaption();
  232. } else {
  233. getSelectionsCaption().setText(rightCaption);
  234. }
  235. captionWrapper.setVisible(hasCaptions);
  236. }
  237. private void removeOptionsCaption() {
  238. if (optionsCaption == null) {
  239. return;
  240. }
  241. if (optionsCaption.getParent() != null) {
  242. captionWrapper.remove(optionsCaption);
  243. }
  244. optionsCaption = null;
  245. }
  246. private void removeSelectionsCaption() {
  247. if (selectionsCaption == null) {
  248. return;
  249. }
  250. if (selectionsCaption.getParent() != null) {
  251. captionWrapper.remove(selectionsCaption);
  252. }
  253. selectionsCaption = null;
  254. }
  255. @Override
  256. public Registration addSelectionChangeListener(
  257. BiConsumer<Set<String>, Set<String>> listener) {
  258. Objects.nonNull(listener);
  259. selectionChangeListeners.add(listener);
  260. return (Registration) () -> selectionChangeListeners.remove(listener);
  261. }
  262. @Override
  263. public void setItems(List<JsonObject> items) {
  264. // filter selected items
  265. List<JsonObject> selection = items.stream()
  266. .filter(item -> MultiSelectWidget.isSelected(item))
  267. .collect(Collectors.toList());
  268. items.removeAll(selection);
  269. updateListBox(optionsListBox, items);
  270. updateListBox(selectionsListBox, selection);
  271. }
  272. private static void updateListBox(ListBox listBox,
  273. List<JsonObject> options) {
  274. for (int i = 0; i < options.size(); i++) {
  275. final JsonObject item = options.get(i);
  276. // reuse existing option if possible
  277. if (i < listBox.getItemCount()) {
  278. listBox.setItemText(i, MultiSelectWidget.getCaption(item));
  279. listBox.setValue(i, MultiSelectWidget.getKey(item));
  280. } else {
  281. listBox.addItem(MultiSelectWidget.getCaption(item),
  282. MultiSelectWidget.getKey(item));
  283. }
  284. }
  285. // remove extra
  286. for (int i = listBox.getItemCount() - 1; i >= options.size(); i--) {
  287. listBox.removeItem(i);
  288. }
  289. }
  290. private static boolean[] getSelectionBitmap(ListBox listBox) {
  291. final boolean[] selectedIndexes = new boolean[listBox.getItemCount()];
  292. for (int i = 0; i < listBox.getItemCount(); i++) {
  293. if (listBox.isItemSelected(i)) {
  294. selectedIndexes[i] = true;
  295. } else {
  296. selectedIndexes[i] = false;
  297. }
  298. }
  299. return selectedIndexes;
  300. }
  301. private void moveSelectedItemsLeftToRight() {
  302. Set<String> movedItems = moveSelectedItems(optionsListBox,
  303. selectionsListBox);
  304. selectionChangeListeners
  305. .forEach(e -> e.accept(movedItems, Collections.emptySet()));
  306. }
  307. private void moveSelectedItemsRightToLeft() {
  308. Set<String> movedItems = moveSelectedItems(selectionsListBox,
  309. optionsListBox);
  310. selectionChangeListeners
  311. .forEach(e -> e.accept(Collections.emptySet(), movedItems));
  312. }
  313. private static Set<String> moveSelectedItems(ListBox source,
  314. ListBox target) {
  315. final boolean[] sel = getSelectionBitmap(source);
  316. final Set<String> movedItems = new HashSet<>();
  317. for (int i = 0; i < sel.length; i++) {
  318. if (sel[i]) {
  319. final int optionIndex = i
  320. - (sel.length - source.getItemCount());
  321. movedItems.add(source.getValue(optionIndex));
  322. // Move selection to another column
  323. final String text = source.getItemText(optionIndex);
  324. final String value = source.getValue(optionIndex);
  325. target.addItem(text, value);
  326. target.setItemSelected(target.getItemCount() - 1, true);
  327. source.removeItem(optionIndex);
  328. }
  329. }
  330. // If no items are left move the focus to the selections
  331. if (source.getItemCount() == 0) {
  332. target.setFocus(true);
  333. } else {
  334. source.setFocus(true);
  335. }
  336. return movedItems;
  337. }
  338. @Override
  339. public void onClick(ClickEvent event) {
  340. if (event.getSource() == addItemsLeftToRightButton) {
  341. moveSelectedItemsLeftToRight();
  342. } else if (event.getSource() == removeItemsRightToLeftButton) {
  343. moveSelectedItemsRightToLeft();
  344. } else if (event.getSource() == optionsListBox) {
  345. // unselect all in other list, to avoid mistakes (i.e wrong button)
  346. final int count = selectionsListBox.getItemCount();
  347. for (int i = 0; i < count; i++) {
  348. selectionsListBox.setItemSelected(i, false);
  349. }
  350. } else if (event.getSource() == selectionsListBox) {
  351. // unselect all in other list, to avoid mistakes (i.e wrong button)
  352. final int count = optionsListBox.getItemCount();
  353. for (int i = 0; i < count; i++) {
  354. optionsListBox.setItemSelected(i, false);
  355. }
  356. }
  357. }
  358. /** For internal use only. May be removed or replaced in the future. */
  359. public void clearInternalHeights() {
  360. selectionsListBox.setHeight("");
  361. optionsListBox.setHeight("");
  362. }
  363. /** For internal use only. May be removed or replaced in the future. */
  364. public void setInternalHeights() {
  365. int captionHeight = WidgetUtil.getRequiredHeight(captionWrapper);
  366. int totalHeight = getOffsetHeight();
  367. String selectHeight = totalHeight - captionHeight + "px";
  368. selectionsListBox.setHeight(selectHeight);
  369. optionsListBox.setHeight(selectHeight);
  370. }
  371. /** For internal use only. May be removed or replaced in the future. */
  372. public void clearInternalWidths() {
  373. String colWidth = DEFAULT_COLUMN_COUNT + "em";
  374. String containerWidth = 2 * DEFAULT_COLUMN_COUNT + 4 + "em";
  375. // Caption wrapper width == optionsSelect + buttons +
  376. // selectionsSelect
  377. String captionWrapperWidth = 2 * DEFAULT_COLUMN_COUNT + 4 - 0.5 + "em";
  378. optionsListBox.setWidth(colWidth);
  379. if (optionsCaption != null) {
  380. optionsCaption.setWidth(colWidth);
  381. }
  382. selectionsListBox.setWidth(colWidth);
  383. if (selectionsCaption != null) {
  384. selectionsCaption.setWidth(colWidth);
  385. }
  386. buttons.setWidth("3.5em");
  387. optionsContainer.setWidth(containerWidth);
  388. captionWrapper.setWidth(captionWrapperWidth);
  389. }
  390. /** For internal use only. May be removed or replaced in the future. */
  391. public void setInternalWidths() {
  392. getElement().getStyle().setPosition(Position.RELATIVE);
  393. int bordersAndPaddings = WidgetUtil
  394. .measureHorizontalPaddingAndBorder(buttons.getElement(), 0);
  395. int buttonWidth = WidgetUtil.getRequiredWidth(buttons);
  396. int totalWidth = getOffsetWidth();
  397. int spaceForSelect = (totalWidth - buttonWidth - bordersAndPaddings)
  398. / 2;
  399. optionsListBox.setWidth(spaceForSelect + "px");
  400. if (optionsCaption != null) {
  401. optionsCaption.setWidth(spaceForSelect + "px");
  402. }
  403. selectionsListBox.setWidth(spaceForSelect + "px");
  404. if (selectionsCaption != null) {
  405. selectionsCaption.setWidth(spaceForSelect + "px");
  406. }
  407. captionWrapper.setWidth("100%");
  408. }
  409. /**
  410. * Sets the tab index.
  411. *
  412. * @param tabIndex
  413. * the tab index to set
  414. */
  415. public void setTabIndex(int tabIndex) {
  416. optionsListBox.setTabIndex(tabIndex);
  417. selectionsListBox.setTabIndex(tabIndex);
  418. addItemsLeftToRightButton.setTabIndex(tabIndex);
  419. removeItemsRightToLeftButton.setTabIndex(tabIndex);
  420. }
  421. /**
  422. * Sets this twin column select as read only, meaning selection cannot be
  423. * changed.
  424. *
  425. * @param readOnly
  426. * {@code true} for read only, {@code false} for not read only
  427. */
  428. public void setReadOnly(boolean readOnly) {
  429. if (this.readOnly != readOnly) {
  430. this.readOnly = readOnly;
  431. updateEnabledState();
  432. }
  433. }
  434. /**
  435. * Returns {@code true} if this twin column select is in read only mode,
  436. * {@code false} if not.
  437. *
  438. * @return {@code true} for read only, {@code false} for not read only
  439. */
  440. public boolean isReadOnly() {
  441. return readOnly;
  442. }
  443. @Override
  444. public void setEnabled(boolean enabled) {
  445. if (this.enabled != enabled) {
  446. this.enabled = enabled;
  447. updateEnabledState();
  448. }
  449. }
  450. @Override
  451. public boolean isEnabled() {
  452. return enabled;
  453. }
  454. private void updateEnabledState() {
  455. boolean enabled = isEnabled() && !isReadOnly();
  456. optionsListBox.setEnabled(enabled);
  457. selectionsListBox.setEnabled(enabled);
  458. addItemsLeftToRightButton.setEnabled(enabled);
  459. removeItemsRightToLeftButton.setEnabled(enabled);
  460. addItemsLeftToRightButton.setStyleName(StyleConstants.DISABLED,
  461. !enabled);
  462. removeItemsRightToLeftButton.setStyleName(StyleConstants.DISABLED,
  463. !enabled);
  464. }
  465. @Override
  466. public void focus() {
  467. optionsListBox.setFocus(true);
  468. }
  469. /**
  470. * Get the key that selects an item in the table. By default it is the Enter
  471. * key but by overriding this you can change the key to whatever you want.
  472. *
  473. * @return the key that selects an item
  474. */
  475. protected int getNavigationSelectKey() {
  476. return KeyCodes.KEY_ENTER;
  477. }
  478. @Override
  479. public void onKeyDown(KeyDownEvent event) {
  480. int keycode = event.getNativeKeyCode();
  481. // Catch tab and move between select:s
  482. if (keycode == KeyCodes.KEY_TAB
  483. && event.getSource() == optionsListBox) {
  484. // Prevent default behavior
  485. event.preventDefault();
  486. // Remove current selections
  487. for (int i = 0; i < optionsListBox.getItemCount(); i++) {
  488. optionsListBox.setItemSelected(i, false);
  489. }
  490. // Focus selections
  491. selectionsListBox.setFocus(true);
  492. }
  493. if (keycode == KeyCodes.KEY_TAB && event.isShiftKeyDown()
  494. && event.getSource() == selectionsListBox) {
  495. // Prevent default behavior
  496. event.preventDefault();
  497. // Remove current selections
  498. for (int i = 0; i < selectionsListBox.getItemCount(); i++) {
  499. selectionsListBox.setItemSelected(i, false);
  500. }
  501. // Focus options
  502. optionsListBox.setFocus(true);
  503. }
  504. if (keycode == getNavigationSelectKey()) {
  505. // Prevent default behavior
  506. event.preventDefault();
  507. // Decide which select the selection was made in
  508. if (event.getSource() == optionsListBox) {
  509. // Prevents the selection to become a single selection when
  510. // using Enter key
  511. // as the selection key (default)
  512. optionsListBox.setFocus(false);
  513. moveSelectedItemsLeftToRight();
  514. } else if (event.getSource() == selectionsListBox) {
  515. // Prevents the selection to become a single selection when
  516. // using Enter key
  517. // as the selection key (default)
  518. selectionsListBox.setFocus(false);
  519. moveSelectedItemsRightToLeft();
  520. }
  521. }
  522. }
  523. @Override
  524. public void onMouseDown(MouseDownEvent event) {
  525. // Ensure that items are deselected when selecting
  526. // from a different source. See #3699 for details.
  527. if (event.getSource() == optionsListBox) {
  528. for (int i = 0; i < selectionsListBox.getItemCount(); i++) {
  529. selectionsListBox.setItemSelected(i, false);
  530. }
  531. } else if (event.getSource() == selectionsListBox) {
  532. for (int i = 0; i < optionsListBox.getItemCount(); i++) {
  533. optionsListBox.setItemSelected(i, false);
  534. }
  535. }
  536. }
  537. @Override
  538. public void onDoubleClick(DoubleClickEvent event) {
  539. if (event.getSource() == optionsListBox) {
  540. moveSelectedItemsLeftToRight();
  541. optionsListBox.setSelectedIndex(-1);
  542. optionsListBox.setFocus(false);
  543. } else if (event.getSource() == selectionsListBox) {
  544. moveSelectedItemsRightToLeft();
  545. selectionsListBox.setSelectedIndex(-1);
  546. selectionsListBox.setFocus(false);
  547. }
  548. }
  549. @Override
  550. public com.google.gwt.user.client.Element getSubPartElement(
  551. String subPart) {
  552. if (SUBPART_OPTION_SELECT.equals(subPart)) {
  553. return optionsListBox.getElement();
  554. } else if (subPart.startsWith(SUBPART_OPTION_SELECT_ITEM)) {
  555. String idx = subPart.substring(SUBPART_OPTION_SELECT_ITEM.length());
  556. return (com.google.gwt.user.client.Element) optionsListBox
  557. .getElement().getChild(Integer.parseInt(idx));
  558. } else if (SUBPART_SELECTION_SELECT.equals(subPart)) {
  559. return selectionsListBox.getElement();
  560. } else if (subPart.startsWith(SUBPART_SELECTION_SELECT_ITEM)) {
  561. String idx = subPart
  562. .substring(SUBPART_SELECTION_SELECT_ITEM.length());
  563. return (com.google.gwt.user.client.Element) selectionsListBox
  564. .getElement().getChild(Integer.parseInt(idx));
  565. } else if (optionsCaption != null
  566. && SUBPART_LEFT_CAPTION.equals(subPart)) {
  567. return optionsCaption.getElement();
  568. } else if (selectionsCaption != null
  569. && SUBPART_RIGHT_CAPTION.equals(subPart)) {
  570. return selectionsCaption.getElement();
  571. } else if (SUBPART_ADD_BUTTON.equals(subPart)) {
  572. return addItemsLeftToRightButton.getElement();
  573. } else if (SUBPART_REMOVE_BUTTON.equals(subPart)) {
  574. return removeItemsRightToLeftButton.getElement();
  575. }
  576. return null;
  577. }
  578. @Override
  579. public String getSubPartName(
  580. com.google.gwt.user.client.Element subElement) {
  581. if (optionsCaption != null
  582. && optionsCaption.getElement().isOrHasChild(subElement)) {
  583. return SUBPART_LEFT_CAPTION;
  584. } else if (selectionsCaption != null
  585. && selectionsCaption.getElement().isOrHasChild(subElement)) {
  586. return SUBPART_RIGHT_CAPTION;
  587. } else if (optionsListBox.getElement().isOrHasChild(subElement)) {
  588. if (optionsListBox.getElement() == subElement) {
  589. return SUBPART_OPTION_SELECT;
  590. } else {
  591. int idx = WidgetUtil.getChildElementIndex(subElement);
  592. return SUBPART_OPTION_SELECT_ITEM + idx;
  593. }
  594. } else if (selectionsListBox.getElement().isOrHasChild(subElement)) {
  595. if (selectionsListBox.getElement() == subElement) {
  596. return SUBPART_SELECTION_SELECT;
  597. } else {
  598. int idx = WidgetUtil.getChildElementIndex(subElement);
  599. return SUBPART_SELECTION_SELECT_ITEM + idx;
  600. }
  601. } else if (addItemsLeftToRightButton.getElement()
  602. .isOrHasChild(subElement)) {
  603. return SUBPART_ADD_BUTTON;
  604. } else if (removeItemsRightToLeftButton.getElement()
  605. .isOrHasChild(subElement)) {
  606. return SUBPART_REMOVE_BUTTON;
  607. }
  608. return null;
  609. }
  610. }