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.

AbstractComponent.java 30KB

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