Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

AbstractComponent.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  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 java.util.ArrayList;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.Locale;
  23. import java.util.StringTokenizer;
  24. import java.util.regex.Matcher;
  25. import java.util.regex.Pattern;
  26. import com.vaadin.event.ActionManager;
  27. import com.vaadin.event.ConnectorActionManager;
  28. import com.vaadin.event.ShortcutListener;
  29. import com.vaadin.server.AbstractClientConnector;
  30. import com.vaadin.server.ComponentSizeValidator;
  31. import com.vaadin.server.ErrorMessage;
  32. import com.vaadin.server.Resource;
  33. import com.vaadin.server.VaadinSession;
  34. import com.vaadin.shared.AbstractComponentState;
  35. import com.vaadin.shared.ComponentConstants;
  36. import com.vaadin.shared.ui.ComponentStateUtil;
  37. import com.vaadin.shared.util.SharedUtil;
  38. import com.vaadin.ui.Field.ValueChangeEvent;
  39. import com.vaadin.util.ReflectTools;
  40. /**
  41. * An abstract class that defines default implementation for the
  42. * {@link Component} interface. Basic UI components that are not derived from an
  43. * external component can inherit this class to easily qualify as Vaadin
  44. * components. Most components in Vaadin do just that.
  45. *
  46. * @author Vaadin Ltd.
  47. * @since 3.0
  48. */
  49. @SuppressWarnings("serial")
  50. public abstract class AbstractComponent extends AbstractClientConnector
  51. implements Component {
  52. /* Private members */
  53. /**
  54. * Application specific data object. The component does not use or modify
  55. * this.
  56. */
  57. private Object applicationData;
  58. /**
  59. * The internal error message of the component.
  60. */
  61. private ErrorMessage componentError = null;
  62. /**
  63. * Locale of this component.
  64. */
  65. private Locale locale;
  66. /**
  67. * The component should receive focus (if {@link Focusable}) when attached.
  68. */
  69. private boolean delayedFocus;
  70. /* Sizeable fields */
  71. private float width = SIZE_UNDEFINED;
  72. private float height = SIZE_UNDEFINED;
  73. private Unit widthUnit = Unit.PIXELS;
  74. private Unit heightUnit = Unit.PIXELS;
  75. private static final Pattern sizePattern = Pattern
  76. .compile(SharedUtil.SIZE_PATTERN);
  77. /**
  78. * Keeps track of the Actions added to this component; the actual
  79. * handling/notifying is delegated, usually to the containing window.
  80. */
  81. private ConnectorActionManager actionManager;
  82. private boolean visible = true;
  83. private HasComponents parent;
  84. private Boolean explicitImmediateValue;
  85. /* Constructor */
  86. /**
  87. * Constructs a new Component.
  88. */
  89. public AbstractComponent() {
  90. // ComponentSizeValidator.setCreationLocation(this);
  91. }
  92. /* Get/Set component properties */
  93. /*
  94. * (non-Javadoc)
  95. *
  96. * @see com.vaadin.ui.Component#setId(java.lang.String)
  97. */
  98. @Override
  99. public void setId(String id) {
  100. getState().id = id;
  101. }
  102. /*
  103. * (non-Javadoc)
  104. *
  105. * @see com.vaadin.ui.Component#getId()
  106. */
  107. @Override
  108. public String getId() {
  109. return getState(false).id;
  110. }
  111. /**
  112. * @deprecated As of 7.0. Use {@link #setId(String)}
  113. */
  114. @Deprecated
  115. public void setDebugId(String id) {
  116. setId(id);
  117. }
  118. /**
  119. * @deprecated As of 7.0. Use {@link #getId()}
  120. */
  121. @Deprecated
  122. public String getDebugId() {
  123. return getId();
  124. }
  125. /*
  126. * Gets the component's style. Don't add a JavaDoc comment here, we use the
  127. * default documentation from implemented interface.
  128. */
  129. @Override
  130. public String getStyleName() {
  131. String s = "";
  132. if (ComponentStateUtil.hasStyles(getState(false))) {
  133. for (final Iterator<String> it = getState(false).styles.iterator(); it
  134. .hasNext();) {
  135. s += it.next();
  136. if (it.hasNext()) {
  137. s += " ";
  138. }
  139. }
  140. }
  141. return s;
  142. }
  143. /*
  144. * Sets the component's style. Don't add a JavaDoc comment here, we use the
  145. * default documentation from implemented interface.
  146. */
  147. @Override
  148. public void setStyleName(String style) {
  149. if (style == null || "".equals(style)) {
  150. getState().styles = null;
  151. return;
  152. }
  153. if (getState().styles == null) {
  154. getState().styles = new ArrayList<String>();
  155. }
  156. List<String> styles = getState().styles;
  157. styles.clear();
  158. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  159. while (tokenizer.hasMoreTokens()) {
  160. styles.add(tokenizer.nextToken());
  161. }
  162. }
  163. @Override
  164. public void setPrimaryStyleName(String style) {
  165. getState().primaryStyleName = style;
  166. }
  167. @Override
  168. public String getPrimaryStyleName() {
  169. return getState(false).primaryStyleName;
  170. }
  171. @Override
  172. public void addStyleName(String style) {
  173. if (style == null || "".equals(style)) {
  174. return;
  175. }
  176. if (style.contains(" ")) {
  177. // Split space separated style names and add them one by one.
  178. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  179. while (tokenizer.hasMoreTokens()) {
  180. addStyleName(tokenizer.nextToken());
  181. }
  182. return;
  183. }
  184. if (getState().styles == null) {
  185. getState().styles = new ArrayList<String>();
  186. }
  187. List<String> styles = getState().styles;
  188. if (!styles.contains(style)) {
  189. styles.add(style);
  190. }
  191. }
  192. @Override
  193. public void removeStyleName(String style) {
  194. if (ComponentStateUtil.hasStyles(getState())) {
  195. StringTokenizer tokenizer = new StringTokenizer(style, " ");
  196. while (tokenizer.hasMoreTokens()) {
  197. getState().styles.remove(tokenizer.nextToken());
  198. }
  199. }
  200. }
  201. /*
  202. * Get's the component's caption. Don't add a JavaDoc comment here, we use
  203. * the default documentation from implemented interface.
  204. */
  205. @Override
  206. public String getCaption() {
  207. return getState(false).caption;
  208. }
  209. /**
  210. * Sets the component's caption <code>String</code>. Caption is the visible
  211. * name of the component. This method will trigger a
  212. * {@link RepaintRequestEvent}.
  213. *
  214. * @param caption
  215. * the new caption <code>String</code> for the component.
  216. */
  217. @Override
  218. public void setCaption(String caption) {
  219. getState().caption = caption;
  220. }
  221. /*
  222. * Don't add a JavaDoc comment here, we use the default documentation from
  223. * implemented interface.
  224. */
  225. @Override
  226. public Locale getLocale() {
  227. if (locale != null) {
  228. return locale;
  229. }
  230. HasComponents parent = getParent();
  231. if (parent != null) {
  232. return parent.getLocale();
  233. }
  234. final VaadinSession session = getSession();
  235. if (session != null) {
  236. return session.getLocale();
  237. }
  238. return null;
  239. }
  240. /**
  241. * Sets the locale of this component.
  242. *
  243. * <pre>
  244. * // Component for which the locale is meaningful
  245. * InlineDateField date = new InlineDateField(&quot;Datum&quot;);
  246. *
  247. * // German language specified with ISO 639-1 language
  248. * // code and ISO 3166-1 alpha-2 country code.
  249. * date.setLocale(new Locale(&quot;de&quot;, &quot;DE&quot;));
  250. *
  251. * date.setResolution(DateField.RESOLUTION_DAY);
  252. * layout.addComponent(date);
  253. * </pre>
  254. *
  255. *
  256. * @param locale
  257. * the locale to become this component's locale.
  258. */
  259. public void setLocale(Locale locale) {
  260. this.locale = locale;
  261. if (locale != null && isAttached()) {
  262. getUI().getLocaleService().addLocale(locale);
  263. }
  264. markAsDirty();
  265. }
  266. /*
  267. * Gets the component's icon resource. Don't add a JavaDoc comment here, we
  268. * use the default documentation from implemented interface.
  269. */
  270. @Override
  271. public Resource getIcon() {
  272. return getResource(ComponentConstants.ICON_RESOURCE);
  273. }
  274. /**
  275. * Sets the component's icon. This method will trigger a
  276. * {@link RepaintRequestEvent}.
  277. *
  278. * @param icon
  279. * the icon to be shown with the component's caption.
  280. */
  281. @Override
  282. public void setIcon(Resource icon) {
  283. setResource(ComponentConstants.ICON_RESOURCE, icon);
  284. }
  285. /*
  286. * (non-Javadoc)
  287. *
  288. * @see com.vaadin.ui.Component#isEnabled()
  289. */
  290. @Override
  291. public boolean isEnabled() {
  292. return getState(false).enabled;
  293. }
  294. /*
  295. * (non-Javadoc)
  296. *
  297. * @see com.vaadin.ui.Component#setEnabled(boolean)
  298. */
  299. @Override
  300. public void setEnabled(boolean enabled) {
  301. getState().enabled = enabled;
  302. }
  303. /*
  304. * (non-Javadoc)
  305. *
  306. * @see com.vaadin.client.Connector#isConnectorEnabled()
  307. */
  308. @Override
  309. public boolean isConnectorEnabled() {
  310. if (!isVisible()) {
  311. return false;
  312. } else if (!isEnabled()) {
  313. return false;
  314. } else if (!super.isConnectorEnabled()) {
  315. return false;
  316. } else if ((getParent() instanceof SelectiveRenderer)
  317. && !((SelectiveRenderer) getParent()).isRendered(this)) {
  318. return false;
  319. } else {
  320. return true;
  321. }
  322. }
  323. public boolean isImmediate() {
  324. if (explicitImmediateValue != null) {
  325. return explicitImmediateValue;
  326. } else if (hasListeners(ValueChangeEvent.class)) {
  327. /*
  328. * Automatic immediate for fields that developers are interested
  329. * about.
  330. */
  331. return true;
  332. } else {
  333. return false;
  334. }
  335. }
  336. /**
  337. * Sets the component's immediate mode to the specified status.
  338. *
  339. * @param immediate
  340. * the boolean value specifying if the component should be in the
  341. * immediate mode after the call.
  342. */
  343. public void setImmediate(boolean immediate) {
  344. explicitImmediateValue = immediate;
  345. getState().immediate = immediate;
  346. }
  347. /*
  348. * (non-Javadoc)
  349. *
  350. * @see com.vaadin.ui.Component#isVisible()
  351. */
  352. @Override
  353. public boolean isVisible() {
  354. return visible;
  355. }
  356. /*
  357. * (non-Javadoc)
  358. *
  359. * @see com.vaadin.ui.Component#setVisible(boolean)
  360. */
  361. @Override
  362. public void setVisible(boolean visible) {
  363. if (isVisible() == visible) {
  364. return;
  365. }
  366. this.visible = visible;
  367. if (visible) {
  368. /*
  369. * If the visibility state is toggled from invisible to visible it
  370. * affects all children (the whole hierarchy) in addition to this
  371. * component.
  372. */
  373. markAsDirtyRecursive();
  374. }
  375. if (getParent() != null) {
  376. // Must always repaint the parent (at least the hierarchy) when
  377. // visibility of a child component changes.
  378. getParent().markAsDirty();
  379. }
  380. }
  381. /*
  382. * (non-Javadoc)
  383. *
  384. * @see com.vaadin.ui.Component#getDescription()
  385. */
  386. @Override
  387. public String getDescription() {
  388. return getState(false).description;
  389. }
  390. /**
  391. * Sets the component's description. See {@link #getDescription()} for more
  392. * information on what the description is. This method will trigger a
  393. * {@link RepaintRequestEvent}.
  394. *
  395. * The description is displayed as HTML in tooltips or directly in certain
  396. * components so care should be taken to avoid creating the possibility for
  397. * HTML injection and possibly XSS vulnerabilities.
  398. *
  399. * @param description
  400. * the new description string for the component.
  401. */
  402. public void setDescription(String description) {
  403. getState().description = description;
  404. }
  405. /*
  406. * Gets the component's parent component. Don't add a JavaDoc comment here,
  407. * we use the default documentation from implemented interface.
  408. */
  409. @Override
  410. public HasComponents getParent() {
  411. return parent;
  412. }
  413. @Override
  414. public void setParent(HasComponents parent) {
  415. // If the parent is not changed, don't do anything
  416. if (parent == this.parent) {
  417. return;
  418. }
  419. if (parent != null && this.parent != null) {
  420. throw new IllegalStateException(getClass().getName()
  421. + " already has a parent.");
  422. }
  423. // Send a detach event if the component is currently attached
  424. if (isAttached()) {
  425. detach();
  426. }
  427. // Connect to new parent
  428. this.parent = parent;
  429. // Send attach event if the component is now attached
  430. if (isAttached()) {
  431. attach();
  432. }
  433. }
  434. /**
  435. * Returns the closest ancestor with the given type.
  436. * <p>
  437. * To find the Window that contains the component, use {@code Window w =
  438. * getParent(Window.class);}
  439. * </p>
  440. *
  441. * @param <T>
  442. * The type of the ancestor
  443. * @param parentType
  444. * The ancestor class we are looking for
  445. * @return The first ancestor that can be assigned to the given class. Null
  446. * if no ancestor with the correct type could be found.
  447. */
  448. public <T extends HasComponents> T findAncestor(Class<T> parentType) {
  449. HasComponents p = getParent();
  450. while (p != null) {
  451. if (parentType.isAssignableFrom(p.getClass())) {
  452. return parentType.cast(p);
  453. }
  454. p = p.getParent();
  455. }
  456. return null;
  457. }
  458. /**
  459. * Gets the error message for this component.
  460. *
  461. * @return ErrorMessage containing the description of the error state of the
  462. * component or null, if the component contains no errors. Extending
  463. * classes should override this method if they support other error
  464. * message types such as validation errors or buffering errors. The
  465. * returned error message contains information about all the errors.
  466. */
  467. public ErrorMessage getErrorMessage() {
  468. return componentError;
  469. }
  470. /**
  471. * Gets the component's error message.
  472. *
  473. * @link Terminal.ErrorMessage#ErrorMessage(String, int)
  474. *
  475. * @return the component's error message.
  476. */
  477. public ErrorMessage getComponentError() {
  478. return componentError;
  479. }
  480. /**
  481. * Sets the component's error message. The message may contain certain XML
  482. * tags, for more information see
  483. *
  484. * @link Component.ErrorMessage#ErrorMessage(String, int)
  485. *
  486. * @param componentError
  487. * the new <code>ErrorMessage</code> of the component.
  488. */
  489. public void setComponentError(ErrorMessage componentError) {
  490. this.componentError = componentError;
  491. fireComponentErrorEvent();
  492. markAsDirty();
  493. }
  494. /*
  495. * Tests if the component is in read-only mode. Don't add a JavaDoc comment
  496. * here, we use the default documentation from implemented interface.
  497. */
  498. @Override
  499. public boolean isReadOnly() {
  500. return getState(false).readOnly;
  501. }
  502. /*
  503. * Sets the component's read-only mode. Don't add a JavaDoc comment here, we
  504. * use the default documentation from implemented interface.
  505. */
  506. @Override
  507. public void setReadOnly(boolean readOnly) {
  508. getState().readOnly = readOnly;
  509. }
  510. /*
  511. * Notify the component that it's attached to a window. Don't add a JavaDoc
  512. * comment here, we use the default documentation from implemented
  513. * interface.
  514. */
  515. @Override
  516. public void attach() {
  517. super.attach();
  518. if (delayedFocus) {
  519. focus();
  520. }
  521. setActionManagerViewer();
  522. if (locale != null) {
  523. getUI().getLocaleService().addLocale(locale);
  524. }
  525. }
  526. /*
  527. * Detach the component from application. Don't add a JavaDoc comment here,
  528. * we use the default documentation from implemented interface.
  529. */
  530. @Override
  531. public void detach() {
  532. super.detach();
  533. if (actionManager != null) {
  534. // Remove any existing viewer. UI cast is just to make the
  535. // compiler happy
  536. actionManager.setViewer((UI) null);
  537. }
  538. }
  539. /**
  540. * Sets the focus for this component if the component is {@link Focusable}.
  541. */
  542. protected void focus() {
  543. if (this instanceof Focusable) {
  544. final VaadinSession session = getSession();
  545. if (session != null) {
  546. getUI().setFocusedComponent((Focusable) this);
  547. delayedFocus = false;
  548. } else {
  549. delayedFocus = true;
  550. }
  551. }
  552. }
  553. /**
  554. * Build CSS compatible string representation of height.
  555. *
  556. * @return CSS height
  557. */
  558. private String getCSSHeight() {
  559. return getHeight() + getHeightUnits().getSymbol();
  560. }
  561. /**
  562. * Build CSS compatible string representation of width.
  563. *
  564. * @return CSS width
  565. */
  566. private String getCSSWidth() {
  567. return getWidth() + getWidthUnits().getSymbol();
  568. }
  569. /**
  570. * Returns the shared state bean with information to be sent from the server
  571. * to the client.
  572. *
  573. * Subclasses should override this method and set any relevant fields of the
  574. * state returned by super.getState().
  575. *
  576. * @since 7.0
  577. *
  578. * @return updated component shared state
  579. */
  580. @Override
  581. protected AbstractComponentState getState() {
  582. return (AbstractComponentState) super.getState();
  583. }
  584. @Override
  585. protected AbstractComponentState getState(boolean markAsDirty) {
  586. return (AbstractComponentState) super.getState(markAsDirty);
  587. }
  588. @Override
  589. public void beforeClientResponse(boolean initial) {
  590. super.beforeClientResponse(initial);
  591. // TODO This logic should be on the client side and the state should
  592. // simply be a data object with "width" and "height".
  593. if (getHeight() >= 0
  594. && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  595. .parentCanDefineHeight(this))) {
  596. getState().height = "" + getCSSHeight();
  597. } else {
  598. getState().height = "";
  599. }
  600. if (getWidth() >= 0
  601. && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  602. .parentCanDefineWidth(this))) {
  603. getState().width = "" + getCSSWidth();
  604. } else {
  605. getState().width = "";
  606. }
  607. ErrorMessage error = getErrorMessage();
  608. if (null != error) {
  609. getState().errorMessage = error.getFormattedHtmlMessage();
  610. } else {
  611. getState().errorMessage = null;
  612. }
  613. getState().immediate = isImmediate();
  614. }
  615. /* General event framework */
  616. private static final Method COMPONENT_EVENT_METHOD = ReflectTools
  617. .findMethod(Component.Listener.class, "componentEvent",
  618. Component.Event.class);
  619. /* Component event framework */
  620. /*
  621. * Registers a new listener to listen events generated by this component.
  622. * Don't add a JavaDoc comment here, we use the default documentation from
  623. * implemented interface.
  624. */
  625. @Override
  626. public void addListener(Component.Listener listener) {
  627. addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  628. }
  629. /*
  630. * Removes a previously registered listener from this component. Don't add a
  631. * JavaDoc comment here, we use the default documentation from implemented
  632. * interface.
  633. */
  634. @Override
  635. public void removeListener(Component.Listener listener) {
  636. removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  637. }
  638. /**
  639. * Emits the component event. It is transmitted to all registered listeners
  640. * interested in such events.
  641. */
  642. protected void fireComponentEvent() {
  643. fireEvent(new Component.Event(this));
  644. }
  645. /**
  646. * Emits the component error event. It is transmitted to all registered
  647. * listeners interested in such events.
  648. */
  649. protected void fireComponentErrorEvent() {
  650. fireEvent(new Component.ErrorEvent(getComponentError(), this));
  651. }
  652. /**
  653. * Sets the data object, that can be used for any application specific data.
  654. * The component does not use or modify this data.
  655. *
  656. * @param data
  657. * the Application specific data.
  658. * @since 3.1
  659. */
  660. public void setData(Object data) {
  661. applicationData = data;
  662. }
  663. /**
  664. * Gets the application specific data. See {@link #setData(Object)}.
  665. *
  666. * @return the Application specific data set with setData function.
  667. * @since 3.1
  668. */
  669. public Object getData() {
  670. return applicationData;
  671. }
  672. /* Sizeable and other size related methods */
  673. /*
  674. * (non-Javadoc)
  675. *
  676. * @see com.vaadin.Sizeable#getHeight()
  677. */
  678. @Override
  679. public float getHeight() {
  680. return height;
  681. }
  682. /*
  683. * (non-Javadoc)
  684. *
  685. * @see com.vaadin.server.Sizeable#getHeightUnits()
  686. */
  687. @Override
  688. public Unit getHeightUnits() {
  689. return heightUnit;
  690. }
  691. /*
  692. * (non-Javadoc)
  693. *
  694. * @see com.vaadin.server.Sizeable#getWidth()
  695. */
  696. @Override
  697. public float getWidth() {
  698. return width;
  699. }
  700. /*
  701. * (non-Javadoc)
  702. *
  703. * @see com.vaadin.server.Sizeable#getWidthUnits()
  704. */
  705. @Override
  706. public Unit getWidthUnits() {
  707. return widthUnit;
  708. }
  709. /*
  710. * (non-Javadoc)
  711. *
  712. * @see com.vaadin.server.Sizeable#setHeight(float, Unit)
  713. */
  714. @Override
  715. public void setHeight(float height, Unit unit) {
  716. if (unit == null) {
  717. throw new IllegalArgumentException("Unit can not be null");
  718. }
  719. this.height = height;
  720. heightUnit = unit;
  721. markAsDirty();
  722. // ComponentSizeValidator.setHeightLocation(this);
  723. }
  724. /*
  725. * (non-Javadoc)
  726. *
  727. * @see com.vaadin.server.Sizeable#setSizeFull()
  728. */
  729. @Override
  730. public void setSizeFull() {
  731. setWidth(100, Unit.PERCENTAGE);
  732. setHeight(100, Unit.PERCENTAGE);
  733. }
  734. /*
  735. * (non-Javadoc)
  736. *
  737. * @see com.vaadin.server.Sizeable#setSizeUndefined()
  738. */
  739. @Override
  740. public void setSizeUndefined() {
  741. setWidth(-1, Unit.PIXELS);
  742. setHeight(-1, Unit.PIXELS);
  743. }
  744. /*
  745. * (non-Javadoc)
  746. *
  747. * @see com.vaadin.server.Sizeable#setWidth(float, Unit)
  748. */
  749. @Override
  750. public void setWidth(float width, Unit unit) {
  751. if (unit == null) {
  752. throw new IllegalArgumentException("Unit can not be null");
  753. }
  754. this.width = width;
  755. widthUnit = unit;
  756. markAsDirty();
  757. // ComponentSizeValidator.setWidthLocation(this);
  758. }
  759. /*
  760. * (non-Javadoc)
  761. *
  762. * @see com.vaadin.server.Sizeable#setWidth(java.lang.String)
  763. */
  764. @Override
  765. public void setWidth(String width) {
  766. Size size = parseStringSize(width);
  767. if (size != null) {
  768. setWidth(size.getSize(), size.getUnit());
  769. } else {
  770. setWidth(-1, Unit.PIXELS);
  771. }
  772. }
  773. /*
  774. * (non-Javadoc)
  775. *
  776. * @see com.vaadin.server.Sizeable#setHeight(java.lang.String)
  777. */
  778. @Override
  779. public void setHeight(String height) {
  780. Size size = parseStringSize(height);
  781. if (size != null) {
  782. setHeight(size.getSize(), size.getUnit());
  783. } else {
  784. setHeight(-1, Unit.PIXELS);
  785. }
  786. }
  787. /*
  788. * Returns array with size in index 0 unit in index 1. Null or empty string
  789. * will produce {-1,Unit#PIXELS}
  790. */
  791. private static Size parseStringSize(String s) {
  792. if (s == null) {
  793. return null;
  794. }
  795. s = s.trim();
  796. if ("".equals(s)) {
  797. return null;
  798. }
  799. float size = 0;
  800. Unit unit = null;
  801. Matcher matcher = sizePattern.matcher(s);
  802. if (matcher.find()) {
  803. size = Float.parseFloat(matcher.group(1));
  804. if (size < 0) {
  805. size = -1;
  806. unit = Unit.PIXELS;
  807. } else {
  808. String symbol = matcher.group(2);
  809. unit = Unit.getUnitFromSymbol(symbol);
  810. }
  811. } else {
  812. throw new IllegalArgumentException("Invalid size argument: \"" + s
  813. + "\" (should match " + sizePattern.pattern() + ")");
  814. }
  815. return new Size(size, unit);
  816. }
  817. private static class Size implements Serializable {
  818. float size;
  819. Unit unit;
  820. public Size(float size, Unit unit) {
  821. this.size = size;
  822. this.unit = unit;
  823. }
  824. public float getSize() {
  825. return size;
  826. }
  827. public Unit getUnit() {
  828. return unit;
  829. }
  830. }
  831. /*
  832. * Actions
  833. */
  834. /**
  835. * Gets the {@link ActionManager} used to manage the
  836. * {@link ShortcutListener}s added to this {@link Field}.
  837. *
  838. * @return the ActionManager in use
  839. */
  840. protected ActionManager getActionManager() {
  841. if (actionManager == null) {
  842. actionManager = new ConnectorActionManager(this);
  843. setActionManagerViewer();
  844. }
  845. return actionManager;
  846. }
  847. /**
  848. * Set a viewer for the action manager to be the parent sub window (if the
  849. * component is in a window) or the UI (otherwise). This is still a
  850. * simplification of the real case as this should be handled by the parent
  851. * VOverlay (on the client side) if the component is inside an VOverlay
  852. * component.
  853. */
  854. private void setActionManagerViewer() {
  855. if (actionManager != null && getUI() != null) {
  856. // Attached and has action manager
  857. Window w = findAncestor(Window.class);
  858. if (w != null) {
  859. actionManager.setViewer(w);
  860. } else {
  861. actionManager.setViewer(getUI());
  862. }
  863. }
  864. }
  865. public void addShortcutListener(ShortcutListener shortcut) {
  866. getActionManager().addAction(shortcut);
  867. }
  868. public void removeShortcutListener(ShortcutListener shortcut) {
  869. if (actionManager != null) {
  870. actionManager.removeAction(shortcut);
  871. }
  872. }
  873. /**
  874. * Determine whether a <code>content</code> component is equal to, or the
  875. * ancestor of this component.
  876. *
  877. * @param content
  878. * the potential ancestor element
  879. * @return <code>true</code> if the relationship holds
  880. */
  881. protected boolean isOrHasAncestor(Component content) {
  882. if (content instanceof HasComponents) {
  883. for (Component parent = this; parent != null; parent = parent
  884. .getParent()) {
  885. if (parent == content) {
  886. return true;
  887. }
  888. }
  889. }
  890. return false;
  891. }
  892. }