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.

Button.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. /*
  2. * Copyright 2000-2014 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.ui;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import com.vaadin.event.Action;
  20. import com.vaadin.event.FieldEvents;
  21. import com.vaadin.event.FieldEvents.BlurEvent;
  22. import com.vaadin.event.FieldEvents.BlurListener;
  23. import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
  24. import com.vaadin.event.FieldEvents.FocusEvent;
  25. import com.vaadin.event.FieldEvents.FocusListener;
  26. import com.vaadin.event.ShortcutAction;
  27. import com.vaadin.event.ShortcutAction.KeyCode;
  28. import com.vaadin.event.ShortcutAction.ModifierKey;
  29. import com.vaadin.event.ShortcutListener;
  30. import com.vaadin.server.Resource;
  31. import com.vaadin.shared.MouseEventDetails;
  32. import com.vaadin.shared.ui.button.ButtonServerRpc;
  33. import com.vaadin.shared.ui.button.ButtonState;
  34. import com.vaadin.ui.Component.Focusable;
  35. import com.vaadin.util.ReflectTools;
  36. /**
  37. * A generic button component.
  38. *
  39. * @author Vaadin Ltd.
  40. * @since 3.0
  41. */
  42. @SuppressWarnings("serial")
  43. public class Button extends AbstractComponent implements
  44. FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, Focusable,
  45. Action.ShortcutNotifier {
  46. private ButtonServerRpc rpc = new ButtonServerRpc() {
  47. @Override
  48. public void click(MouseEventDetails mouseEventDetails) {
  49. fireClick(mouseEventDetails);
  50. }
  51. @Override
  52. public void disableOnClick() throws RuntimeException {
  53. setEnabled(false);
  54. // Makes sure the enabled=false state is noticed at once - otherwise
  55. // a following setEnabled(true) call might have no effect. see
  56. // ticket #10030
  57. getUI().getConnectorTracker().getDiffState(Button.this)
  58. .put("enabled", false);
  59. }
  60. };
  61. FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(this) {
  62. @Override
  63. protected void fireEvent(Event event) {
  64. Button.this.fireEvent(event);
  65. }
  66. };
  67. /**
  68. * Creates a new push button.
  69. */
  70. public Button() {
  71. registerRpc(rpc);
  72. registerRpc(focusBlurRpc);
  73. }
  74. /**
  75. * Creates a new push button with the given caption.
  76. *
  77. * @param caption
  78. * the Button caption.
  79. */
  80. public Button(String caption) {
  81. this();
  82. setCaption(caption);
  83. }
  84. /**
  85. * Creates a new push button with the given icon.
  86. *
  87. * @param icon
  88. * the icon
  89. */
  90. public Button(Resource icon) {
  91. this();
  92. setIcon(icon);
  93. }
  94. /**
  95. * Creates a new push button with the given caption and icon.
  96. *
  97. * @param caption
  98. * the caption
  99. * @param icon
  100. * the icon
  101. */
  102. public Button(String caption, Resource icon) {
  103. this();
  104. setCaption(caption);
  105. setIcon(icon);
  106. }
  107. /**
  108. * Creates a new push button with a click listener.
  109. *
  110. * @param caption
  111. * the Button caption.
  112. * @param listener
  113. * the Button click listener.
  114. */
  115. public Button(String caption, ClickListener listener) {
  116. this(caption);
  117. addListener(listener);
  118. }
  119. /**
  120. * Click event. This event is thrown, when the button is clicked.
  121. *
  122. * @author Vaadin Ltd.
  123. * @since 3.0
  124. */
  125. public static class ClickEvent extends Component.Event {
  126. private final MouseEventDetails details;
  127. /**
  128. * New instance of text change event.
  129. *
  130. * @param source
  131. * the Source of the event.
  132. */
  133. public ClickEvent(Component source) {
  134. super(source);
  135. details = null;
  136. }
  137. /**
  138. * Constructor with mouse details
  139. *
  140. * @param source
  141. * The source where the click took place
  142. * @param details
  143. * Details about the mouse click
  144. */
  145. public ClickEvent(Component source, MouseEventDetails details) {
  146. super(source);
  147. this.details = details;
  148. }
  149. /**
  150. * Gets the Button where the event occurred.
  151. *
  152. * @return the Source of the event.
  153. */
  154. public Button getButton() {
  155. return (Button) getSource();
  156. }
  157. /**
  158. * Returns the mouse position (x coordinate) when the click took place.
  159. * The position is relative to the browser client area.
  160. *
  161. * @return The mouse cursor x position or -1 if unknown
  162. */
  163. public int getClientX() {
  164. if (null != details) {
  165. return details.getClientX();
  166. } else {
  167. return -1;
  168. }
  169. }
  170. /**
  171. * Returns the mouse position (y coordinate) when the click took place.
  172. * The position is relative to the browser client area.
  173. *
  174. * @return The mouse cursor y position or -1 if unknown
  175. */
  176. public int getClientY() {
  177. if (null != details) {
  178. return details.getClientY();
  179. } else {
  180. return -1;
  181. }
  182. }
  183. /**
  184. * Returns the relative mouse position (x coordinate) when the click
  185. * took place. The position is relative to the clicked component.
  186. *
  187. * @return The mouse cursor x position relative to the clicked layout
  188. * component or -1 if no x coordinate available
  189. */
  190. public int getRelativeX() {
  191. if (null != details) {
  192. return details.getRelativeX();
  193. } else {
  194. return -1;
  195. }
  196. }
  197. /**
  198. * Returns the relative mouse position (y coordinate) when the click
  199. * took place. The position is relative to the clicked component.
  200. *
  201. * @return The mouse cursor y position relative to the clicked layout
  202. * component or -1 if no y coordinate available
  203. */
  204. public int getRelativeY() {
  205. if (null != details) {
  206. return details.getRelativeY();
  207. } else {
  208. return -1;
  209. }
  210. }
  211. /**
  212. * Checks if the Alt key was down when the mouse event took place.
  213. *
  214. * @return true if Alt was down when the event occured, false otherwise
  215. * or if unknown
  216. */
  217. public boolean isAltKey() {
  218. if (null != details) {
  219. return details.isAltKey();
  220. } else {
  221. return false;
  222. }
  223. }
  224. /**
  225. * Checks if the Ctrl key was down when the mouse event took place.
  226. *
  227. * @return true if Ctrl was pressed when the event occured, false
  228. * otherwise or if unknown
  229. */
  230. public boolean isCtrlKey() {
  231. if (null != details) {
  232. return details.isCtrlKey();
  233. } else {
  234. return false;
  235. }
  236. }
  237. /**
  238. * Checks if the Meta key was down when the mouse event took place.
  239. *
  240. * @return true if Meta was pressed when the event occured, false
  241. * otherwise or if unknown
  242. */
  243. public boolean isMetaKey() {
  244. if (null != details) {
  245. return details.isMetaKey();
  246. } else {
  247. return false;
  248. }
  249. }
  250. /**
  251. * Checks if the Shift key was down when the mouse event took place.
  252. *
  253. * @return true if Shift was pressed when the event occured, false
  254. * otherwise or if unknown
  255. */
  256. public boolean isShiftKey() {
  257. if (null != details) {
  258. return details.isShiftKey();
  259. } else {
  260. return false;
  261. }
  262. }
  263. }
  264. /**
  265. * Interface for listening for a {@link ClickEvent} fired by a
  266. * {@link Component}.
  267. *
  268. * @author Vaadin Ltd.
  269. * @since 3.0
  270. */
  271. public interface ClickListener extends Serializable {
  272. public static final Method BUTTON_CLICK_METHOD = ReflectTools
  273. .findMethod(ClickListener.class, "buttonClick",
  274. ClickEvent.class);
  275. /**
  276. * Called when a {@link Button} has been clicked. A reference to the
  277. * button is given by {@link ClickEvent#getButton()}.
  278. *
  279. * @param event
  280. * An event containing information about the click.
  281. */
  282. public void buttonClick(ClickEvent event);
  283. }
  284. /**
  285. * Adds the button click listener.
  286. *
  287. * @param listener
  288. * the Listener to be added.
  289. */
  290. public void addClickListener(ClickListener listener) {
  291. addListener(ClickEvent.class, listener,
  292. ClickListener.BUTTON_CLICK_METHOD);
  293. }
  294. /**
  295. * @deprecated As of 7.0, replaced by
  296. * {@link #addClickListener(ClickListener)}
  297. **/
  298. @Deprecated
  299. public void addListener(ClickListener listener) {
  300. addClickListener(listener);
  301. }
  302. /**
  303. * Removes the button click listener.
  304. *
  305. * @param listener
  306. * the Listener to be removed.
  307. */
  308. public void removeClickListener(ClickListener listener) {
  309. removeListener(ClickEvent.class, listener,
  310. ClickListener.BUTTON_CLICK_METHOD);
  311. }
  312. /**
  313. * @deprecated As of 7.0, replaced by
  314. * {@link #removeClickListener(ClickListener)}
  315. **/
  316. @Deprecated
  317. public void removeListener(ClickListener listener) {
  318. removeClickListener(listener);
  319. }
  320. /**
  321. * Simulates a button click, notifying all server-side listeners.
  322. *
  323. * No action is taken is the button is disabled.
  324. */
  325. public void click() {
  326. if (isEnabled() && !isReadOnly()) {
  327. fireClick();
  328. }
  329. }
  330. /**
  331. * Fires a click event to all listeners without any event details.
  332. *
  333. * In subclasses, override {@link #fireClick(MouseEventDetails)} instead of
  334. * this method.
  335. */
  336. protected void fireClick() {
  337. fireEvent(new Button.ClickEvent(this));
  338. }
  339. /**
  340. * Fires a click event to all listeners.
  341. *
  342. * @param details
  343. * MouseEventDetails from which keyboard modifiers and other
  344. * information about the mouse click can be obtained. If the
  345. * button was clicked by a keyboard event, some of the fields may
  346. * be empty/undefined.
  347. */
  348. protected void fireClick(MouseEventDetails details) {
  349. fireEvent(new Button.ClickEvent(this, details));
  350. }
  351. @Override
  352. public void addBlurListener(BlurListener listener) {
  353. addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  354. BlurListener.blurMethod);
  355. }
  356. /**
  357. * @deprecated As of 7.0, replaced by {@link #addBlurListener(BlurListener)}
  358. **/
  359. @Override
  360. @Deprecated
  361. public void addListener(BlurListener listener) {
  362. addBlurListener(listener);
  363. }
  364. @Override
  365. public void removeBlurListener(BlurListener listener) {
  366. removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
  367. }
  368. /**
  369. * @deprecated As of 7.0, replaced by
  370. * {@link #removeBlurListener(BlurListener)}
  371. **/
  372. @Override
  373. @Deprecated
  374. public void removeListener(BlurListener listener) {
  375. removeBlurListener(listener);
  376. }
  377. @Override
  378. public void addFocusListener(FocusListener listener) {
  379. addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  380. FocusListener.focusMethod);
  381. }
  382. /**
  383. * @deprecated As of 7.0, replaced by
  384. * {@link #addFocusListener(FocusListener)}
  385. **/
  386. @Override
  387. @Deprecated
  388. public void addListener(FocusListener listener) {
  389. addFocusListener(listener);
  390. }
  391. @Override
  392. public void removeFocusListener(FocusListener listener) {
  393. removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
  394. }
  395. /**
  396. * @deprecated As of 7.0, replaced by
  397. * {@link #removeFocusListener(FocusListener)}
  398. **/
  399. @Override
  400. @Deprecated
  401. public void removeListener(FocusListener listener) {
  402. removeFocusListener(listener);
  403. }
  404. /*
  405. * Actions
  406. */
  407. protected ClickShortcut clickShortcut;
  408. /**
  409. * Makes it possible to invoke a click on this button by pressing the given
  410. * {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
  411. * The shortcut is global (bound to the containing Window).
  412. *
  413. * @param keyCode
  414. * the keycode for invoking the shortcut
  415. * @param modifiers
  416. * the (optional) modifiers for invoking the shortcut, null for
  417. * none
  418. */
  419. public void setClickShortcut(int keyCode, int... modifiers) {
  420. if (clickShortcut != null) {
  421. removeShortcutListener(clickShortcut);
  422. }
  423. clickShortcut = new ClickShortcut(this, keyCode, modifiers);
  424. addShortcutListener(clickShortcut);
  425. getState().clickShortcutKeyCode = clickShortcut.getKeyCode();
  426. }
  427. /**
  428. * Removes the keyboard shortcut previously set with
  429. * {@link #setClickShortcut(int, int...)}.
  430. */
  431. public void removeClickShortcut() {
  432. if (clickShortcut != null) {
  433. removeShortcutListener(clickShortcut);
  434. clickShortcut = null;
  435. getState().clickShortcutKeyCode = 0;
  436. }
  437. }
  438. /**
  439. * A {@link ShortcutListener} specifically made to define a keyboard
  440. * shortcut that invokes a click on the given button.
  441. *
  442. */
  443. public static class ClickShortcut extends ShortcutListener {
  444. protected Button button;
  445. /**
  446. * Creates a keyboard shortcut for clicking the given button using the
  447. * shorthand notation defined in {@link ShortcutAction}.
  448. *
  449. * @param button
  450. * to be clicked when the shortcut is invoked
  451. * @param shorthandCaption
  452. * the caption with shortcut keycode and modifiers indicated
  453. */
  454. public ClickShortcut(Button button, String shorthandCaption) {
  455. super(shorthandCaption);
  456. this.button = button;
  457. }
  458. /**
  459. * Creates a keyboard shortcut for clicking the given button using the
  460. * given {@link KeyCode} and {@link ModifierKey}s.
  461. *
  462. * @param button
  463. * to be clicked when the shortcut is invoked
  464. * @param keyCode
  465. * KeyCode to react to
  466. * @param modifiers
  467. * optional modifiers for shortcut
  468. */
  469. public ClickShortcut(Button button, int keyCode, int... modifiers) {
  470. super(null, keyCode, modifiers);
  471. this.button = button;
  472. }
  473. /**
  474. * Creates a keyboard shortcut for clicking the given button using the
  475. * given {@link KeyCode}.
  476. *
  477. * @param button
  478. * to be clicked when the shortcut is invoked
  479. * @param keyCode
  480. * KeyCode to react to
  481. */
  482. public ClickShortcut(Button button, int keyCode) {
  483. this(button, keyCode, null);
  484. }
  485. @Override
  486. public void handleAction(Object sender, Object target) {
  487. button.click();
  488. }
  489. }
  490. /**
  491. * Determines if a button is automatically disabled when clicked. See
  492. * {@link #setDisableOnClick(boolean)} for details.
  493. *
  494. * @return true if the button is disabled when clicked, false otherwise
  495. */
  496. public boolean isDisableOnClick() {
  497. return getState(false).disableOnClick;
  498. }
  499. /**
  500. * Determines if a button is automatically disabled when clicked. If this is
  501. * set to true the button will be automatically disabled when clicked,
  502. * typically to prevent (accidental) extra clicks on a button.
  503. * <p>
  504. * Note that this is only used when the click comes from the user, not when
  505. * calling {@link #click()}.
  506. * </p>
  507. *
  508. * @param disableOnClick
  509. * true to disable button when it is clicked, false otherwise
  510. */
  511. public void setDisableOnClick(boolean disableOnClick) {
  512. getState().disableOnClick = disableOnClick;
  513. }
  514. /*
  515. * (non-Javadoc)
  516. *
  517. * @see com.vaadin.ui.Component.Focusable#getTabIndex()
  518. */
  519. @Override
  520. public int getTabIndex() {
  521. return getState(false).tabIndex;
  522. }
  523. /*
  524. * (non-Javadoc)
  525. *
  526. * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
  527. */
  528. @Override
  529. public void setTabIndex(int tabIndex) {
  530. getState().tabIndex = tabIndex;
  531. }
  532. @Override
  533. public void focus() {
  534. // Overridden only to make public
  535. super.focus();
  536. }
  537. @Override
  538. protected ButtonState getState() {
  539. return (ButtonState) super.getState();
  540. }
  541. @Override
  542. protected ButtonState getState(boolean markAsDirty) {
  543. return (ButtonState) super.getState(markAsDirty);
  544. }
  545. /**
  546. * Sets the component's icon and alt text.
  547. *
  548. * An alt text is shown when an image could not be loaded, and read by
  549. * assisitve devices.
  550. *
  551. * @param icon
  552. * the icon to be shown with the component's caption.
  553. * @param iconAltText
  554. * String to use as alt text
  555. */
  556. public void setIcon(Resource icon, String iconAltText) {
  557. super.setIcon(icon);
  558. getState().iconAltText = iconAltText == null ? "" : iconAltText;
  559. }
  560. /**
  561. * Returns the icon's alt text.
  562. *
  563. * @return String with the alt text
  564. */
  565. public String getIconAlternateText() {
  566. return getState(false).iconAltText;
  567. }
  568. public void setIconAlternateText(String iconAltText) {
  569. getState().iconAltText = iconAltText;
  570. }
  571. /**
  572. * Set whether the caption text is rendered as HTML or not. You might need
  573. * to retheme button to allow higher content than the original text style.
  574. *
  575. * If set to true, the captions are passed to the browser as html and the
  576. * developer is responsible for ensuring no harmful html is used. If set to
  577. * false, the content is passed to the browser as plain text.
  578. *
  579. * @param htmlContentAllowed
  580. * <code>true</code> if caption is rendered as HTML,
  581. * <code>false</code> otherwise
  582. */
  583. public void setHtmlContentAllowed(boolean htmlContentAllowed) {
  584. getState().htmlContentAllowed = htmlContentAllowed;
  585. }
  586. /**
  587. * Return HTML rendering setting
  588. *
  589. * @return <code>true</code> if the caption text is to be rendered as HTML,
  590. * <code>false</code> otherwise
  591. */
  592. public boolean isHtmlContentAllowed() {
  593. return getState(false).htmlContentAllowed;
  594. }
  595. }