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 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.lang.reflect.Method;
  7. import java.util.Map;
  8. import com.vaadin.event.Action;
  9. import com.vaadin.event.FieldEvents;
  10. import com.vaadin.event.FieldEvents.BlurEvent;
  11. import com.vaadin.event.FieldEvents.BlurListener;
  12. import com.vaadin.event.FieldEvents.FocusEvent;
  13. import com.vaadin.event.FieldEvents.FocusListener;
  14. import com.vaadin.event.ShortcutAction;
  15. import com.vaadin.event.ShortcutAction.KeyCode;
  16. import com.vaadin.event.ShortcutAction.ModifierKey;
  17. import com.vaadin.event.ShortcutListener;
  18. import com.vaadin.terminal.gwt.client.ComponentState;
  19. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  20. import com.vaadin.terminal.gwt.client.ui.ButtonConnector;
  21. import com.vaadin.terminal.gwt.client.ui.ButtonConnector.ButtonServerRpc;
  22. import com.vaadin.terminal.gwt.client.ui.ButtonState;
  23. import com.vaadin.terminal.gwt.server.RpcTarget;
  24. import com.vaadin.tools.ReflectTools;
  25. import com.vaadin.ui.ClientWidget.LoadStyle;
  26. import com.vaadin.ui.Component.Focusable;
  27. /**
  28. * A generic button component.
  29. *
  30. * @author Vaadin Ltd.
  31. * @version
  32. * @VERSION@
  33. * @since 3.0
  34. */
  35. @SuppressWarnings("serial")
  36. @ClientWidget(value = ButtonConnector.class, loadStyle = LoadStyle.EAGER)
  37. public class Button extends AbstractComponent implements
  38. FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, Focusable,
  39. Action.ShortcutNotifier, RpcTarget {
  40. /**
  41. * Creates a new push button.
  42. */
  43. public Button() {
  44. // TODO take the implementation out of an anonymous class?
  45. registerRpcImplementation(new ButtonServerRpc() {
  46. public void click(MouseEventDetails mouseEventDetails) {
  47. fireClick(mouseEventDetails);
  48. }
  49. public void disableOnClick() {
  50. // Could be optimized so the button is not repainted because of
  51. // this (client side has already disabled the button)
  52. setEnabled(false);
  53. }
  54. }, ButtonServerRpc.class);
  55. }
  56. /**
  57. * Creates a new push button with the given caption.
  58. *
  59. * @param caption
  60. * the Button caption.
  61. */
  62. public Button(String caption) {
  63. this();
  64. setCaption(caption);
  65. }
  66. /**
  67. * Creates a new push button with a click listener.
  68. *
  69. * @param caption
  70. * the Button caption.
  71. * @param listener
  72. * the Button click listener.
  73. */
  74. public Button(String caption, ClickListener listener) {
  75. this(caption);
  76. addListener(listener);
  77. }
  78. /**
  79. * Invoked when the value of a variable has changed. Button listeners are
  80. * notified if the button is clicked.
  81. *
  82. * @param source
  83. * @param variables
  84. */
  85. @Override
  86. public void changeVariables(Object source, Map<String, Object> variables) {
  87. super.changeVariables(source, variables);
  88. if (variables.containsKey(FocusEvent.EVENT_ID)) {
  89. fireEvent(new FocusEvent(this));
  90. }
  91. if (variables.containsKey(BlurEvent.EVENT_ID)) {
  92. fireEvent(new BlurEvent(this));
  93. }
  94. }
  95. /**
  96. * Click event. This event is thrown, when the button is clicked.
  97. *
  98. * @author Vaadin Ltd.
  99. * @version
  100. * @VERSION@
  101. * @since 3.0
  102. */
  103. public class ClickEvent extends Component.Event {
  104. private final MouseEventDetails details;
  105. /**
  106. * New instance of text change event.
  107. *
  108. * @param source
  109. * the Source of the event.
  110. */
  111. public ClickEvent(Component source) {
  112. super(source);
  113. details = null;
  114. }
  115. /**
  116. * Constructor with mouse details
  117. *
  118. * @param source
  119. * The source where the click took place
  120. * @param details
  121. * Details about the mouse click
  122. */
  123. public ClickEvent(Component source, MouseEventDetails details) {
  124. super(source);
  125. this.details = details;
  126. }
  127. /**
  128. * Gets the Button where the event occurred.
  129. *
  130. * @return the Source of the event.
  131. */
  132. public Button getButton() {
  133. return (Button) getSource();
  134. }
  135. /**
  136. * Returns the mouse position (x coordinate) when the click took place.
  137. * The position is relative to the browser client area.
  138. *
  139. * @return The mouse cursor x position or -1 if unknown
  140. */
  141. public int getClientX() {
  142. if (null != details) {
  143. return details.getClientX();
  144. } else {
  145. return -1;
  146. }
  147. }
  148. /**
  149. * Returns the mouse position (y coordinate) when the click took place.
  150. * The position is relative to the browser client area.
  151. *
  152. * @return The mouse cursor y position or -1 if unknown
  153. */
  154. public int getClientY() {
  155. if (null != details) {
  156. return details.getClientY();
  157. } else {
  158. return -1;
  159. }
  160. }
  161. /**
  162. * Returns the relative mouse position (x coordinate) when the click
  163. * took place. The position is relative to the clicked component.
  164. *
  165. * @return The mouse cursor x position relative to the clicked layout
  166. * component or -1 if no x coordinate available
  167. */
  168. public int getRelativeX() {
  169. if (null != details) {
  170. return details.getRelativeX();
  171. } else {
  172. return -1;
  173. }
  174. }
  175. /**
  176. * Returns the relative mouse position (y coordinate) when the click
  177. * took place. The position is relative to the clicked component.
  178. *
  179. * @return The mouse cursor y position relative to the clicked layout
  180. * component or -1 if no y coordinate available
  181. */
  182. public int getRelativeY() {
  183. if (null != details) {
  184. return details.getRelativeY();
  185. } else {
  186. return -1;
  187. }
  188. }
  189. /**
  190. * Checks if the Alt key was down when the mouse event took place.
  191. *
  192. * @return true if Alt was down when the event occured, false otherwise
  193. * or if unknown
  194. */
  195. public boolean isAltKey() {
  196. if (null != details) {
  197. return details.isAltKey();
  198. } else {
  199. return false;
  200. }
  201. }
  202. /**
  203. * Checks if the Ctrl key was down when the mouse event took place.
  204. *
  205. * @return true if Ctrl was pressed when the event occured, false
  206. * otherwise or if unknown
  207. */
  208. public boolean isCtrlKey() {
  209. if (null != details) {
  210. return details.isCtrlKey();
  211. } else {
  212. return false;
  213. }
  214. }
  215. /**
  216. * Checks if the Meta key was down when the mouse event took place.
  217. *
  218. * @return true if Meta was pressed when the event occured, false
  219. * otherwise or if unknown
  220. */
  221. public boolean isMetaKey() {
  222. if (null != details) {
  223. return details.isMetaKey();
  224. } else {
  225. return false;
  226. }
  227. }
  228. /**
  229. * Checks if the Shift key was down when the mouse event took place.
  230. *
  231. * @return true if Shift was pressed when the event occured, false
  232. * otherwise or if unknown
  233. */
  234. public boolean isShiftKey() {
  235. if (null != details) {
  236. return details.isShiftKey();
  237. } else {
  238. return false;
  239. }
  240. }
  241. }
  242. /**
  243. * Interface for listening for a {@link ClickEvent} fired by a
  244. * {@link Component}.
  245. *
  246. * @author Vaadin Ltd.
  247. * @version
  248. * @VERSION@
  249. * @since 3.0
  250. */
  251. public interface ClickListener extends Serializable {
  252. public static final Method BUTTON_CLICK_METHOD = ReflectTools
  253. .findMethod(ClickListener.class, "buttonClick",
  254. ClickEvent.class);
  255. /**
  256. * Called when a {@link Button} has been clicked. A reference to the
  257. * button is given by {@link ClickEvent#getButton()}.
  258. *
  259. * @param event
  260. * An event containing information about the click.
  261. */
  262. public void buttonClick(ClickEvent event);
  263. }
  264. /**
  265. * Adds the button click listener.
  266. *
  267. * @param listener
  268. * the Listener to be added.
  269. */
  270. public void addListener(ClickListener listener) {
  271. addListener(ClickEvent.class, listener,
  272. ClickListener.BUTTON_CLICK_METHOD);
  273. }
  274. /**
  275. * Removes the button click listener.
  276. *
  277. * @param listener
  278. * the Listener to be removed.
  279. */
  280. public void removeListener(ClickListener listener) {
  281. removeListener(ClickEvent.class, listener,
  282. ClickListener.BUTTON_CLICK_METHOD);
  283. }
  284. /**
  285. * Fires a click event to all listeners without any event details.
  286. *
  287. * In subclasses, override {@link #fireClick(MouseEventDetails)} instead of
  288. * this method.
  289. */
  290. protected void fireClick() {
  291. fireEvent(new Button.ClickEvent(this));
  292. }
  293. /**
  294. * Fires a click event to all listeners.
  295. *
  296. * @param details
  297. * MouseEventDetails from which keyboard modifiers and other
  298. * information about the mouse click can be obtained. If the
  299. * button was clicked by a keyboard event, some of the fields may
  300. * be empty/undefined.
  301. */
  302. protected void fireClick(MouseEventDetails details) {
  303. fireEvent(new Button.ClickEvent(this, details));
  304. }
  305. public void addListener(BlurListener listener) {
  306. addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  307. BlurListener.blurMethod);
  308. }
  309. public void removeListener(BlurListener listener) {
  310. removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
  311. }
  312. public void addListener(FocusListener listener) {
  313. addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  314. FocusListener.focusMethod);
  315. }
  316. public void removeListener(FocusListener listener) {
  317. removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
  318. }
  319. /*
  320. * Actions
  321. */
  322. protected ClickShortcut clickShortcut;
  323. private int tabIndex = 0;
  324. /**
  325. * Makes it possible to invoke a click on this button by pressing the given
  326. * {@link KeyCode} and (optional) {@link ModifierKey}s.<br/>
  327. * The shortcut is global (bound to the containing Window).
  328. *
  329. * @param keyCode
  330. * the keycode for invoking the shortcut
  331. * @param modifiers
  332. * the (optional) modifiers for invoking the shortcut, null for
  333. * none
  334. */
  335. public void setClickShortcut(int keyCode, int... modifiers) {
  336. if (clickShortcut != null) {
  337. removeShortcutListener(clickShortcut);
  338. }
  339. clickShortcut = new ClickShortcut(this, keyCode, modifiers);
  340. addShortcutListener(clickShortcut);
  341. getState().setClickShortcutKeyCode(clickShortcut.getKeyCode());
  342. }
  343. /**
  344. * Removes the keyboard shortcut previously set with
  345. * {@link #setClickShortcut(int, int...)}.
  346. */
  347. public void removeClickShortcut() {
  348. if (clickShortcut != null) {
  349. removeShortcutListener(clickShortcut);
  350. clickShortcut = null;
  351. getState().setClickShortcutKeyCode(0);
  352. }
  353. }
  354. /**
  355. * A {@link ShortcutListener} specifically made to define a keyboard
  356. * shortcut that invokes a click on the given button.
  357. *
  358. */
  359. public static class ClickShortcut extends ShortcutListener {
  360. protected Button button;
  361. /**
  362. * Creates a keyboard shortcut for clicking the given button using the
  363. * shorthand notation defined in {@link ShortcutAction}.
  364. *
  365. * @param button
  366. * to be clicked when the shortcut is invoked
  367. * @param shorthandCaption
  368. * the caption with shortcut keycode and modifiers indicated
  369. */
  370. public ClickShortcut(Button button, String shorthandCaption) {
  371. super(shorthandCaption);
  372. this.button = button;
  373. }
  374. /**
  375. * Creates a keyboard shortcut for clicking the given button using the
  376. * given {@link KeyCode} and {@link ModifierKey}s.
  377. *
  378. * @param button
  379. * to be clicked when the shortcut is invoked
  380. * @param keyCode
  381. * KeyCode to react to
  382. * @param modifiers
  383. * optional modifiers for shortcut
  384. */
  385. public ClickShortcut(Button button, int keyCode, int... modifiers) {
  386. super(null, keyCode, modifiers);
  387. this.button = button;
  388. }
  389. /**
  390. * Creates a keyboard shortcut for clicking the given button using the
  391. * given {@link KeyCode}.
  392. *
  393. * @param button
  394. * to be clicked when the shortcut is invoked
  395. * @param keyCode
  396. * KeyCode to react to
  397. */
  398. public ClickShortcut(Button button, int keyCode) {
  399. this(button, keyCode, null);
  400. }
  401. @Override
  402. public void handleAction(Object sender, Object target) {
  403. if (button.isEnabled() && !button.isReadOnly()) {
  404. button.fireClick();
  405. }
  406. }
  407. }
  408. /**
  409. * Determines if a button is automatically disabled when clicked. See
  410. * {@link #setDisableOnClick(boolean)} for details.
  411. *
  412. * @return true if the button is disabled when clicked, false otherwise
  413. */
  414. public boolean isDisableOnClick() {
  415. return getState().isDisableOnClick();
  416. }
  417. /**
  418. * Determines if a button is automatically disabled when clicked. If this is
  419. * set to true the button will be automatically disabled when clicked,
  420. * typically to prevent (accidental) extra clicks on a button.
  421. *
  422. * @param disableOnClick
  423. * true to disable button when it is clicked, false otherwise
  424. */
  425. public void setDisableOnClick(boolean disableOnClick) {
  426. getState().setDisableOnClick(disableOnClick);
  427. requestRepaint();
  428. }
  429. public int getTabIndex() {
  430. return tabIndex;
  431. }
  432. public void setTabIndex(int tabIndex) {
  433. this.tabIndex = tabIndex;
  434. }
  435. @Override
  436. public void focus() {
  437. // Overridden only to make public
  438. super.focus();
  439. }
  440. @Override
  441. protected ComponentState createState() {
  442. return new ButtonState();
  443. }
  444. @Override
  445. public ButtonState getState() {
  446. return (ButtonState) super.getState();
  447. }
  448. }