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.

AbstractComponentConnector.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import com.google.gwt.core.client.GWT;
  6. import com.google.gwt.user.client.DOM;
  7. import com.google.gwt.user.client.ui.FocusWidget;
  8. import com.google.gwt.user.client.ui.Focusable;
  9. import com.google.gwt.user.client.ui.Widget;
  10. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  11. import com.vaadin.terminal.gwt.client.ComponentConnector;
  12. import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
  13. import com.vaadin.terminal.gwt.client.ComponentState;
  14. import com.vaadin.terminal.gwt.client.ConnectorMap;
  15. import com.vaadin.terminal.gwt.client.LayoutManager;
  16. import com.vaadin.terminal.gwt.client.TooltipInfo;
  17. import com.vaadin.terminal.gwt.client.UIDL;
  18. import com.vaadin.terminal.gwt.client.VConsole;
  19. import com.vaadin.terminal.gwt.client.communication.ServerRpc;
  20. import com.vaadin.terminal.gwt.client.communication.ServerRpc.InitializableClientToServerRpc;
  21. import com.vaadin.terminal.gwt.client.communication.SharedState;
  22. public abstract class AbstractComponentConnector extends AbstractConnector
  23. implements ComponentConnector {
  24. private ComponentContainerConnector parent;
  25. // Generic UIDL parameter names, to be moved to shared state.
  26. // Attributes are here mainly if they apply to all paintable widgets or
  27. // affect captions - otherwise, they are in the relevant subclasses.
  28. // For e.g. item or context specific attributes, subclasses may use separate
  29. // constants, which may refer to these.
  30. // Not all references to the string literals have been converted to use
  31. // these!
  32. public static final String ATTRIBUTE_REQUIRED = "required";
  33. public static final String ATTRIBUTE_ERROR = "error";
  34. public static final String ATTRIBUTE_HIDEERRORS = "hideErrors";
  35. private Widget widget;
  36. /* State variables */
  37. private boolean visible = true;
  38. // shared state from the server to the client
  39. private ComponentState state;
  40. private String declaredWidth = "";
  41. private String declaredHeight = "";
  42. /**
  43. * Default constructor
  44. */
  45. public AbstractComponentConnector() {
  46. }
  47. /**
  48. * Creates and returns the widget for this VPaintableWidget. This method
  49. * should only be called once when initializing the paintable.
  50. *
  51. * @return
  52. */
  53. protected abstract Widget createWidget();
  54. /**
  55. * Returns the widget associated with this paintable. The widget returned by
  56. * this method must not changed during the life time of the paintable.
  57. *
  58. * @return The widget associated with this paintable
  59. */
  60. public Widget getWidget() {
  61. if (widget == null) {
  62. widget = createWidget();
  63. }
  64. return widget;
  65. }
  66. /**
  67. * Returns the shared state object for a paintable widget.
  68. *
  69. * A new state instance is created using {@link #createState()} if none has
  70. * been set by the server.
  71. *
  72. * If overriding this method to return a more specific type, also
  73. * {@link #createState()} must be overridden.
  74. *
  75. * @return current shared state (not null)
  76. */
  77. public ComponentState getState() {
  78. if (state == null) {
  79. state = createState();
  80. }
  81. return state;
  82. }
  83. /**
  84. * Creates a new instance of a shared state object for the widget. Normally,
  85. * the state instance is created by the server and sent to the client before
  86. * being used - this method is used if no shared state has been sent by the
  87. * server.
  88. *
  89. * When overriding {@link #getState()}, also {@link #createState()} should
  90. * be overridden to match it.
  91. *
  92. * @return newly created component shared state instance
  93. */
  94. protected ComponentState createState() {
  95. return GWT.create(ComponentState.class);
  96. }
  97. protected static boolean isRealUpdate(UIDL uidl) {
  98. return !isCachedUpdate(uidl) && !uidl.getBooleanAttribute("invisible")
  99. && !uidl.hasAttribute("deferred");
  100. }
  101. protected static boolean isCachedUpdate(UIDL uidl) {
  102. return uidl.getBooleanAttribute("cached");
  103. }
  104. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  105. if (isCachedUpdate(uidl)) {
  106. return;
  107. }
  108. ConnectorMap paintableMap = ConnectorMap.get(getConnection());
  109. // register the listened events by the server-side to the event-handler
  110. // of the component
  111. paintableMap.registerEventListenersFromUIDL(getConnectorId(), uidl);
  112. // Visibility
  113. setVisible(!uidl.getBooleanAttribute("invisible"), uidl);
  114. if (uidl.getId().startsWith("PID_S")) {
  115. DOM.setElementProperty(getWidget().getElement(), "id", uidl.getId()
  116. .substring(5));
  117. }
  118. if (!isVisible()) {
  119. // component is invisible, delete old size to notify parent, if
  120. // later made visible
  121. paintableMap.setOffsetSize(this, null);
  122. return;
  123. }
  124. if (!isRealUpdate(uidl)) {
  125. return;
  126. }
  127. /*
  128. * Disabled state may affect (override) tabindex so the order must be
  129. * first setting tabindex, then enabled state.
  130. */
  131. if (uidl.hasAttribute("tabindex") && getWidget() instanceof Focusable) {
  132. ((Focusable) getWidget()).setTabIndex(uidl
  133. .getIntAttribute("tabindex"));
  134. }
  135. if (getWidget() instanceof FocusWidget) {
  136. FocusWidget fw = (FocusWidget) getWidget();
  137. fw.setEnabled(isEnabled());
  138. }
  139. // Style names
  140. String styleName = getStyleNameFromUIDL(getWidget()
  141. .getStylePrimaryName(), uidl, getWidget() instanceof Field,
  142. this);
  143. getWidget().setStyleName(styleName);
  144. // Update tooltip
  145. TooltipInfo tooltipInfo = paintableMap.getTooltipInfo(this, null);
  146. if (getState().hasDescription()) {
  147. tooltipInfo.setTitle(getState().getDescription());
  148. } else {
  149. tooltipInfo.setTitle(null);
  150. }
  151. // add error info to tooltip if present
  152. if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
  153. tooltipInfo.setErrorUidl(uidl.getErrors());
  154. } else {
  155. tooltipInfo.setErrorUidl(null);
  156. }
  157. // Set captions
  158. if (delegateCaptionHandling()) {
  159. getParent().updateCaption(this, uidl);
  160. }
  161. /*
  162. * updateComponentSize need to be after caption update so caption can be
  163. * taken into account
  164. */
  165. updateComponentSize();
  166. }
  167. private void updateComponentSize() {
  168. SharedState state = getState();
  169. String w = "";
  170. String h = "";
  171. if (null != state || !(state instanceof ComponentState)) {
  172. ComponentState componentState = (ComponentState) state;
  173. // TODO move logging to VUIDLBrowser and VDebugConsole
  174. // VConsole.log("Paintable state for "
  175. // + getPaintableMap().getPid(paintable) + ": "
  176. // + String.valueOf(state.getState()));
  177. h = componentState.getHeight();
  178. w = componentState.getWidth();
  179. } else {
  180. // TODO move logging to VUIDLBrowser and VDebugConsole
  181. VConsole.log("No state for paintable " + getConnectorId()
  182. + " in VAbstractPaintableWidget.updateComponentSize()");
  183. }
  184. // Parent should be updated if either dimension changed between relative
  185. // and non-relative
  186. if (w.endsWith("%") != declaredWidth.endsWith("%")) {
  187. ComponentContainerConnector parent = getParent();
  188. if (parent instanceof ManagedLayout) {
  189. getLayoutManager().setWidthNeedsUpdate((ManagedLayout) parent);
  190. }
  191. }
  192. if (h.endsWith("%") != declaredHeight.endsWith("%")) {
  193. ComponentContainerConnector parent = getParent();
  194. if (parent instanceof ManagedLayout) {
  195. getLayoutManager().setHeightNeedsUpdate((ManagedLayout) parent);
  196. }
  197. }
  198. declaredWidth = w;
  199. declaredHeight = h;
  200. // Set defined sizes
  201. Widget component = getWidget();
  202. component.setStyleName("v-has-width", !isUndefinedWidth());
  203. component.setStyleName("v-has-height", !isUndefinedHeight());
  204. component.setHeight(h);
  205. component.setWidth(w);
  206. }
  207. public boolean isRelativeHeight() {
  208. return declaredHeight.endsWith("%");
  209. }
  210. public boolean isRelativeWidth() {
  211. return declaredWidth.endsWith("%");
  212. }
  213. public boolean isUndefinedHeight() {
  214. return declaredHeight.length() == 0;
  215. }
  216. public boolean isUndefinedWidth() {
  217. return declaredWidth.length() == 0;
  218. }
  219. public String getDeclaredHeight() {
  220. return declaredHeight;
  221. }
  222. public String getDeclaredWidth() {
  223. return declaredWidth;
  224. }
  225. /*
  226. * (non-Javadoc)
  227. *
  228. * @see com.vaadin.terminal.gwt.client.Connector#isEnabled()
  229. */
  230. public boolean isEnabled() {
  231. if (!getState().isEnabled()) {
  232. return false;
  233. }
  234. if (getParent() == null) {
  235. return true;
  236. } else {
  237. return getParent().isEnabled();
  238. }
  239. }
  240. /**
  241. * Return true if parent handles caption, false if the paintable handles the
  242. * caption itself.
  243. *
  244. *
  245. * @deprecated This should always return true and all components should let
  246. * the parent handle the caption and use other attributes for
  247. * internal texts in the component
  248. * @return
  249. */
  250. @Deprecated
  251. protected boolean delegateCaptionHandling() {
  252. return true;
  253. }
  254. /**
  255. * Sets the visible state for this paintable.
  256. *
  257. * @param visible
  258. * true if the paintable should be made visible, false otherwise
  259. * @param captionUidl
  260. * The UIDL that is passed to the parent and onwards to VCaption
  261. * if the caption needs to be updated as a result of the
  262. * visibility change.
  263. */
  264. protected void setVisible(boolean visible, UIDL captionUidl) {
  265. boolean wasVisible = this.visible;
  266. this.visible = visible;
  267. getWidget().setVisible(visible);
  268. if (wasVisible != visible) {
  269. // Changed invisibile <-> visible
  270. if (wasVisible && delegateCaptionHandling()) {
  271. // Must hide caption when component is hidden
  272. getParent().updateCaption(this, captionUidl);
  273. }
  274. }
  275. }
  276. protected boolean isVisible() {
  277. return visible;
  278. }
  279. /**
  280. * Generates the style name for the widget based on the given primary style
  281. * name (typically returned by Widget.getPrimaryStyleName()) and the UIDL
  282. * and shared state of the component. An additional "modified" style name
  283. * can be added if the field parameter is set to true.
  284. *
  285. * @param primaryStyleName
  286. * @param uidl
  287. * @param state
  288. * component shared state
  289. * @param field
  290. * @return
  291. */
  292. protected static String getStyleNameFromUIDL(String primaryStyleName,
  293. UIDL uidl, boolean field, ComponentConnector connector) {
  294. ComponentState state = connector.getState();
  295. StringBuffer styleBuf = new StringBuffer();
  296. styleBuf.append(primaryStyleName);
  297. styleBuf.append(" v-paintable");
  298. // Uses connector methods to enable connectors to take hierarchy or
  299. // multiple state variables into account
  300. if (!connector.isEnabled()) {
  301. styleBuf.append(" ");
  302. styleBuf.append(ApplicationConnection.DISABLED_CLASSNAME);
  303. }
  304. if (connector.isReadOnly()) {
  305. styleBuf.append(" ");
  306. styleBuf.append("v-readonly");
  307. }
  308. // add additional styles as css classes, prefixed with component default
  309. // stylename
  310. if (state.hasStyles()) {
  311. for (String style : state.getStyles()) {
  312. styleBuf.append(" ");
  313. styleBuf.append(primaryStyleName);
  314. styleBuf.append("-");
  315. styleBuf.append(style);
  316. styleBuf.append(" ");
  317. styleBuf.append(style);
  318. }
  319. }
  320. // TODO Move to AbstractFieldConnector
  321. // add modified classname to Fields
  322. if (field && uidl.hasAttribute("modified")) {
  323. styleBuf.append(" ");
  324. styleBuf.append(ApplicationConnection.MODIFIED_CLASSNAME);
  325. }
  326. // add error classname to components w/ error
  327. if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
  328. styleBuf.append(" ");
  329. styleBuf.append(primaryStyleName);
  330. styleBuf.append(ApplicationConnection.ERROR_CLASSNAME_EXT);
  331. }
  332. // add required style to required components
  333. if (uidl.hasAttribute(ATTRIBUTE_REQUIRED)) {
  334. styleBuf.append(" ");
  335. styleBuf.append(primaryStyleName);
  336. styleBuf.append(ApplicationConnection.REQUIRED_CLASSNAME_EXT);
  337. }
  338. return styleBuf.toString();
  339. }
  340. /*
  341. * (non-Javadoc)
  342. *
  343. * @see com.vaadin.terminal.gwt.client.ComponentConnector#isReadOnly()
  344. */
  345. @Deprecated
  346. public boolean isReadOnly() {
  347. return getState().isReadOnly();
  348. }
  349. /**
  350. * Sets the shared state for the paintable widget.
  351. *
  352. * @param new shared state (must be compatible with the return value of
  353. * {@link #getState()} - {@link ComponentState} if
  354. * {@link #getState()} is not overridden
  355. */
  356. public final void setState(SharedState state) {
  357. this.state = (ComponentState) state;
  358. }
  359. /**
  360. * Initialize the given RPC proxy object so it is connected to this
  361. * paintable.
  362. *
  363. * @param clientToServerRpc
  364. * The RPC instance to initialize. Must have been created using
  365. * GWT.create().
  366. */
  367. protected <T extends ServerRpc> T initRPC(T clientToServerRpc) {
  368. ((InitializableClientToServerRpc) clientToServerRpc).initRpc(getConnectorId(),
  369. getConnection());
  370. return clientToServerRpc;
  371. }
  372. public LayoutManager getLayoutManager() {
  373. return LayoutManager.get(getConnection());
  374. }
  375. /*
  376. * (non-Javadoc)
  377. *
  378. * @see com.vaadin.terminal.gwt.client.Connector#getParent()
  379. */
  380. public ComponentContainerConnector getParent() {
  381. return parent;
  382. }
  383. /*
  384. * (non-Javadoc)
  385. *
  386. * @see
  387. * com.vaadin.terminal.gwt.client.Connector#setParent(com.vaadin.terminal
  388. * .gwt.client.ComponentContainerConnector)
  389. */
  390. public void setParent(ComponentContainerConnector parent) {
  391. this.parent = parent;
  392. }
  393. }