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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.ArrayList;
  6. import java.util.Iterator;
  7. import com.google.gwt.event.dom.client.ClickEvent;
  8. import com.google.gwt.event.dom.client.DoubleClickEvent;
  9. import com.google.gwt.event.dom.client.DoubleClickHandler;
  10. import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
  11. import com.google.gwt.event.dom.client.KeyCodes;
  12. import com.google.gwt.event.dom.client.KeyDownEvent;
  13. import com.google.gwt.event.dom.client.KeyDownHandler;
  14. import com.google.gwt.event.dom.client.MouseDownEvent;
  15. import com.google.gwt.event.dom.client.MouseDownHandler;
  16. import com.google.gwt.event.shared.HandlerRegistration;
  17. import com.google.gwt.user.client.DOM;
  18. import com.google.gwt.user.client.ui.FlowPanel;
  19. import com.google.gwt.user.client.ui.HTML;
  20. import com.google.gwt.user.client.ui.ListBox;
  21. import com.google.gwt.user.client.ui.Panel;
  22. import com.vaadin.terminal.gwt.client.UIDL;
  23. public class VTwinColSelect extends VOptionGroupBase implements KeyDownHandler,
  24. MouseDownHandler, DoubleClickHandler {
  25. private static final String CLASSNAME = "v-select-twincol";
  26. private static final int VISIBLE_COUNT = 10;
  27. private static final int DEFAULT_COLUMN_COUNT = 10;
  28. private final DoubleClickListBox options;
  29. private final DoubleClickListBox selections;
  30. private final VButton add;
  31. private final VButton remove;
  32. private final FlowPanel buttons;
  33. private final Panel panel;
  34. private boolean widthSet = false;
  35. /**
  36. * A ListBox which catches double clicks
  37. *
  38. */
  39. public class DoubleClickListBox extends ListBox implements
  40. HasDoubleClickHandlers {
  41. public DoubleClickListBox(boolean isMultipleSelect) {
  42. super(isMultipleSelect);
  43. }
  44. public DoubleClickListBox() {
  45. super();
  46. }
  47. public HandlerRegistration addDoubleClickHandler(
  48. DoubleClickHandler handler) {
  49. return addDomHandler(handler, DoubleClickEvent.getType());
  50. }
  51. }
  52. public VTwinColSelect() {
  53. super(CLASSNAME);
  54. options = new DoubleClickListBox();
  55. options.addClickHandler(this);
  56. options.addDoubleClickHandler(this);
  57. selections = new DoubleClickListBox();
  58. selections.addClickHandler(this);
  59. selections.addDoubleClickHandler(this);
  60. options.setVisibleItemCount(VISIBLE_COUNT);
  61. selections.setVisibleItemCount(VISIBLE_COUNT);
  62. options.setStyleName(CLASSNAME + "-options");
  63. selections.setStyleName(CLASSNAME + "-selections");
  64. buttons = new FlowPanel();
  65. buttons.setStyleName(CLASSNAME + "-buttons");
  66. add = new VButton();
  67. add.setText(">>");
  68. add.addClickHandler(this);
  69. remove = new VButton();
  70. remove.setText("<<");
  71. remove.addClickHandler(this);
  72. panel = ((Panel) optionsContainer);
  73. panel.add(options);
  74. buttons.add(add);
  75. final HTML br = new HTML("<span/>");
  76. br.setStyleName(CLASSNAME + "-deco");
  77. buttons.add(br);
  78. buttons.add(remove);
  79. panel.add(buttons);
  80. panel.add(selections);
  81. options.addKeyDownHandler(this);
  82. options.addMouseDownHandler(this);
  83. selections.addMouseDownHandler(this);
  84. selections.addKeyDownHandler(this);
  85. }
  86. @Override
  87. protected void buildOptions(UIDL uidl) {
  88. final boolean enabled = !isDisabled() && !isReadonly();
  89. options.setMultipleSelect(isMultiselect());
  90. selections.setMultipleSelect(isMultiselect());
  91. options.setEnabled(enabled);
  92. selections.setEnabled(enabled);
  93. add.setEnabled(enabled);
  94. remove.setEnabled(enabled);
  95. options.clear();
  96. selections.clear();
  97. for (final Iterator i = uidl.getChildIterator(); i.hasNext();) {
  98. final UIDL optionUidl = (UIDL) i.next();
  99. if (optionUidl.hasAttribute("selected")) {
  100. selections.addItem(optionUidl.getStringAttribute("caption"),
  101. optionUidl.getStringAttribute("key"));
  102. } else {
  103. options.addItem(optionUidl.getStringAttribute("caption"),
  104. optionUidl.getStringAttribute("key"));
  105. }
  106. }
  107. int cols = -1;
  108. if (getColumns() > 0) {
  109. cols = getColumns();
  110. } else if (!widthSet) {
  111. cols = DEFAULT_COLUMN_COUNT;
  112. }
  113. if (cols >= 0) {
  114. options.setWidth(cols + "em");
  115. selections.setWidth(cols + "em");
  116. buttons.setWidth("3.5em");
  117. optionsContainer.setWidth((2 * cols + 4) + "em");
  118. }
  119. if (getRows() > 0) {
  120. options.setVisibleItemCount(getRows());
  121. selections.setVisibleItemCount(getRows());
  122. }
  123. }
  124. @Override
  125. protected String[] getSelectedItems() {
  126. final ArrayList<String> selectedItemKeys = new ArrayList<String>();
  127. for (int i = 0; i < selections.getItemCount(); i++) {
  128. selectedItemKeys.add(selections.getValue(i));
  129. }
  130. return selectedItemKeys.toArray(new String[selectedItemKeys.size()]);
  131. }
  132. private boolean[] getItemsToAdd() {
  133. final boolean[] selectedIndexes = new boolean[options.getItemCount()];
  134. for (int i = 0; i < options.getItemCount(); i++) {
  135. if (options.isItemSelected(i)) {
  136. selectedIndexes[i] = true;
  137. } else {
  138. selectedIndexes[i] = false;
  139. }
  140. }
  141. return selectedIndexes;
  142. }
  143. private boolean[] getItemsToRemove() {
  144. final boolean[] selectedIndexes = new boolean[selections.getItemCount()];
  145. for (int i = 0; i < selections.getItemCount(); i++) {
  146. if (selections.isItemSelected(i)) {
  147. selectedIndexes[i] = true;
  148. } else {
  149. selectedIndexes[i] = false;
  150. }
  151. }
  152. return selectedIndexes;
  153. }
  154. private void addItem() {
  155. final boolean[] sel = getItemsToAdd();
  156. for (int i = 0; i < sel.length; i++) {
  157. if (sel[i]) {
  158. final int optionIndex = i
  159. - (sel.length - options.getItemCount());
  160. selectedKeys.add(options.getValue(optionIndex));
  161. // Move selection to another column
  162. final String text = options.getItemText(optionIndex);
  163. final String value = options.getValue(optionIndex);
  164. selections.addItem(text, value);
  165. selections.setItemSelected(selections.getItemCount() - 1, true);
  166. options.removeItem(optionIndex);
  167. if (options.getItemCount() > 0) {
  168. options.setItemSelected(optionIndex > 0 ? optionIndex - 1
  169. : 0, true);
  170. }
  171. }
  172. }
  173. // If no items are left move the focus to the selections
  174. if (options.getItemCount() == 0) {
  175. selections.setFocus(true);
  176. } else {
  177. options.setFocus(true);
  178. }
  179. client.updateVariable(id, "selected",
  180. selectedKeys.toArray(new String[selectedKeys.size()]),
  181. isImmediate());
  182. }
  183. private void removeItem() {
  184. final boolean[] sel = getItemsToRemove();
  185. for (int i = 0; i < sel.length; i++) {
  186. if (sel[i]) {
  187. final int selectionIndex = i
  188. - (sel.length - selections.getItemCount());
  189. selectedKeys.remove(selections.getValue(selectionIndex));
  190. // Move selection to another column
  191. final String text = selections.getItemText(selectionIndex);
  192. final String value = selections.getValue(selectionIndex);
  193. options.addItem(text, value);
  194. options.setItemSelected(options.getItemCount() - 1, true);
  195. selections.removeItem(selectionIndex);
  196. if (selections.getItemCount() > 0) {
  197. selections.setItemSelected(
  198. selectionIndex > 0 ? selectionIndex - 1 : 0, true);
  199. }
  200. }
  201. }
  202. // If no items are left move the focus to the selections
  203. if (selections.getItemCount() == 0) {
  204. options.setFocus(true);
  205. } else {
  206. selections.setFocus(true);
  207. }
  208. client.updateVariable(id, "selected",
  209. selectedKeys.toArray(new String[selectedKeys.size()]),
  210. isImmediate());
  211. }
  212. @Override
  213. public void onClick(ClickEvent event) {
  214. super.onClick(event);
  215. if (event.getSource() == add) {
  216. addItem();
  217. } else if (event.getSource() == remove) {
  218. removeItem();
  219. } else if (event.getSource() == options) {
  220. // unselect all in other list, to avoid mistakes (i.e wrong button)
  221. final int c = selections.getItemCount();
  222. for (int i = 0; i < c; i++) {
  223. selections.setItemSelected(i, false);
  224. }
  225. } else if (event.getSource() == selections) {
  226. // unselect all in other list, to avoid mistakes (i.e wrong button)
  227. final int c = options.getItemCount();
  228. for (int i = 0; i < c; i++) {
  229. options.setItemSelected(i, false);
  230. }
  231. }
  232. }
  233. @Override
  234. public void setHeight(String height) {
  235. super.setHeight(height);
  236. if ("".equals(height)) {
  237. options.setHeight("");
  238. selections.setHeight("");
  239. } else {
  240. setFullHeightInternals();
  241. }
  242. }
  243. private void setFullHeightInternals() {
  244. options.setHeight("100%");
  245. selections.setHeight("100%");
  246. }
  247. @Override
  248. public void setWidth(String width) {
  249. super.setWidth(width);
  250. if (!"".equals(width) && width != null) {
  251. setRelativeInternalWidths();
  252. }
  253. }
  254. private void setRelativeInternalWidths() {
  255. DOM.setStyleAttribute(getElement(), "position", "relative");
  256. buttons.setWidth("15%");
  257. options.setWidth("42%");
  258. selections.setWidth("42%");
  259. widthSet = true;
  260. }
  261. @Override
  262. protected void setTabIndex(int tabIndex) {
  263. options.setTabIndex(tabIndex);
  264. selections.setTabIndex(tabIndex);
  265. add.setTabIndex(tabIndex);
  266. remove.setTabIndex(tabIndex);
  267. }
  268. public void focus() {
  269. options.setFocus(true);
  270. }
  271. /**
  272. * Get the key that selects an item in the table. By default it is the Enter
  273. * key but by overriding this you can change the key to whatever you want.
  274. *
  275. * @return
  276. */
  277. protected int getNavigationSelectKey() {
  278. return KeyCodes.KEY_ENTER;
  279. }
  280. /*
  281. * (non-Javadoc)
  282. *
  283. * @see
  284. * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt
  285. * .event.dom.client.KeyDownEvent)
  286. */
  287. public void onKeyDown(KeyDownEvent event) {
  288. int keycode = event.getNativeKeyCode();
  289. // Catch tab and move between select:s
  290. if (keycode == KeyCodes.KEY_TAB && event.getSource() == options) {
  291. // Prevent default behavior
  292. event.preventDefault();
  293. // Remove current selections
  294. for (int i = 0; i < options.getItemCount(); i++) {
  295. options.setItemSelected(i, false);
  296. }
  297. // Focus selections
  298. selections.setFocus(true);
  299. }
  300. if (keycode == KeyCodes.KEY_TAB && event.isShiftKeyDown()
  301. && event.getSource() == selections) {
  302. // Prevent default behavior
  303. event.preventDefault();
  304. // Remove current selections
  305. for (int i = 0; i < selections.getItemCount(); i++) {
  306. selections.setItemSelected(i, false);
  307. }
  308. // Focus options
  309. options.setFocus(true);
  310. }
  311. if (keycode == getNavigationSelectKey()) {
  312. // Prevent default behavior
  313. event.preventDefault();
  314. // Decide which select the selection was made in
  315. if (event.getSource() == options) {
  316. // Prevents the selection to become a single selection when
  317. // using Enter key
  318. // as the selection key (default)
  319. options.setFocus(false);
  320. addItem();
  321. } else if (event.getSource() == selections) {
  322. // Prevents the selection to become a single selection when
  323. // using Enter key
  324. // as the selection key (default)
  325. selections.setFocus(false);
  326. removeItem();
  327. }
  328. }
  329. }
  330. /*
  331. * (non-Javadoc)
  332. *
  333. * @see
  334. * com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google
  335. * .gwt.event.dom.client.MouseDownEvent)
  336. */
  337. public void onMouseDown(MouseDownEvent event) {
  338. // Ensure that items are deselected when selecting
  339. // from a different source. See #3699 for details.
  340. if (event.getSource() == options) {
  341. for (int i = 0; i < selections.getItemCount(); i++) {
  342. selections.setItemSelected(i, false);
  343. }
  344. } else if (event.getSource() == selections) {
  345. for (int i = 0; i < options.getItemCount(); i++) {
  346. options.setItemSelected(i, false);
  347. }
  348. }
  349. }
  350. /*
  351. * (non-Javadoc)
  352. *
  353. * @see
  354. * com.google.gwt.event.dom.client.DoubleClickHandler#onDoubleClick(com.
  355. * google.gwt.event.dom.client.DoubleClickEvent)
  356. */
  357. public void onDoubleClick(DoubleClickEvent event) {
  358. if (event.getSource() == options) {
  359. addItem();
  360. options.setSelectedIndex(-1);
  361. options.setFocus(false);
  362. } else if (event.getSource() == selections) {
  363. removeItem();
  364. selections.setSelectedIndex(-1);
  365. selections.setFocus(false);
  366. }
  367. }
  368. }