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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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. ComponentContainerConnector parent = getParent();
  160. if (parent != null) {
  161. parent.updateCaption(this, uidl);
  162. } else {
  163. VConsole.error("Parent of connector "
  164. + getClass().getName()
  165. + " ("
  166. + getConnectorId()
  167. + ") is null. This is typically an indication of a broken component hierarchy");
  168. }
  169. }
  170. /*
  171. * updateComponentSize need to be after caption update so caption can be
  172. * taken into account
  173. */
  174. updateComponentSize();
  175. }
  176. private void updateComponentSize() {
  177. SharedState state = getState();
  178. String w = "";
  179. String h = "";
  180. if (null != state || !(state instanceof ComponentState)) {
  181. ComponentState componentState = (ComponentState) state;
  182. // TODO move logging to VUIDLBrowser and VDebugConsole
  183. // VConsole.log("Paintable state for "
  184. // + getPaintableMap().getPid(paintable) + ": "
  185. // + String.valueOf(state.getState()));
  186. h = componentState.getHeight();
  187. w = componentState.getWidth();
  188. } else {
  189. // TODO move logging to VUIDLBrowser and VDebugConsole
  190. VConsole.log("No state for paintable " + getConnectorId()
  191. + " in VAbstractPaintableWidget.updateComponentSize()");
  192. }
  193. // Parent should be updated if either dimension changed between relative
  194. // and non-relative
  195. if (w.endsWith("%") != declaredWidth.endsWith("%")) {
  196. ComponentContainerConnector parent = getParent();
  197. if (parent instanceof ManagedLayout) {
  198. getLayoutManager().setWidthNeedsUpdate((ManagedLayout) parent);
  199. }
  200. }
  201. if (h.endsWith("%") != declaredHeight.endsWith("%")) {
  202. ComponentContainerConnector parent = getParent();
  203. if (parent instanceof ManagedLayout) {
  204. getLayoutManager().setHeightNeedsUpdate((ManagedLayout) parent);
  205. }
  206. }
  207. declaredWidth = w;
  208. declaredHeight = h;
  209. // Set defined sizes
  210. Widget component = getWidget();
  211. component.setStyleName("v-has-width", !isUndefinedWidth());
  212. component.setStyleName("v-has-height", !isUndefinedHeight());
  213. component.setHeight(h);
  214. component.setWidth(w);
  215. }
  216. public boolean isRelativeHeight() {
  217. return declaredHeight.endsWith("%");
  218. }
  219. public boolean isRelativeWidth() {
  220. return declaredWidth.endsWith("%");
  221. }
  222. public boolean isUndefinedHeight() {
  223. return declaredHeight.length() == 0;
  224. }
  225. public boolean isUndefinedWidth() {
  226. return declaredWidth.length() == 0;
  227. }
  228. public String getDeclaredHeight() {
  229. return declaredHeight;
  230. }
  231. public String getDeclaredWidth() {
  232. return declaredWidth;
  233. }
  234. /*
  235. * (non-Javadoc)
  236. *
  237. * @see com.vaadin.terminal.gwt.client.Connector#isEnabled()
  238. */
  239. public boolean isEnabled() {
  240. if (!getState().isEnabled()) {
  241. return false;
  242. }
  243. if (getParent() == null) {
  244. return true;
  245. } else {
  246. return getParent().isEnabled();
  247. }
  248. }
  249. /**
  250. * Return true if parent handles caption, false if the paintable handles the
  251. * caption itself.
  252. *
  253. *
  254. * @deprecated This should always return true and all components should let
  255. * the parent handle the caption and use other attributes for
  256. * internal texts in the component
  257. * @return
  258. */
  259. @Deprecated
  260. protected boolean delegateCaptionHandling() {
  261. return true;
  262. }
  263. /**
  264. * Sets the visible state for this paintable.
  265. *
  266. * @param visible
  267. * true if the paintable should be made visible, false otherwise
  268. * @param captionUidl
  269. * The UIDL that is passed to the parent and onwards to VCaption
  270. * if the caption needs to be updated as a result of the
  271. * visibility change.
  272. */
  273. protected void setVisible(boolean visible, UIDL captionUidl) {
  274. boolean wasVisible = this.visible;
  275. this.visible = visible;
  276. getWidget().setVisible(visible);
  277. if (wasVisible != visible) {
  278. // Changed invisibile <-> visible
  279. if (wasVisible && delegateCaptionHandling()) {
  280. // Must hide caption when component is hidden
  281. getParent().updateCaption(this, captionUidl);
  282. }
  283. }
  284. }
  285. protected boolean isVisible() {
  286. return visible;
  287. }
  288. /**
  289. * Generates the style name for the widget based on the given primary style
  290. * name (typically returned by Widget.getPrimaryStyleName()) and the UIDL
  291. * and shared state of the component. An additional "modified" style name
  292. * can be added if the field parameter is set to true.
  293. *
  294. * @param primaryStyleName
  295. * @param uidl
  296. * @param state
  297. * component shared state
  298. * @param field
  299. * @return
  300. */
  301. protected static String getStyleNameFromUIDL(String primaryStyleName,
  302. UIDL uidl, boolean field, ComponentConnector connector) {
  303. ComponentState state = connector.getState();
  304. StringBuffer styleBuf = new StringBuffer();
  305. styleBuf.append(primaryStyleName);
  306. styleBuf.append(" v-paintable");
  307. // Uses connector methods to enable connectors to take hierarchy or
  308. // multiple state variables into account
  309. if (!connector.isEnabled()) {
  310. styleBuf.append(" ");
  311. styleBuf.append(ApplicationConnection.DISABLED_CLASSNAME);
  312. }
  313. if (connector.isReadOnly()) {
  314. styleBuf.append(" ");
  315. styleBuf.append("v-readonly");
  316. }
  317. // add additional styles as css classes, prefixed with component default
  318. // stylename
  319. if (state.hasStyles()) {
  320. for (String style : state.getStyles()) {
  321. styleBuf.append(" ");
  322. styleBuf.append(primaryStyleName);
  323. styleBuf.append("-");
  324. styleBuf.append(style);
  325. styleBuf.append(" ");
  326. styleBuf.append(style);
  327. }
  328. }
  329. // TODO Move to AbstractFieldConnector
  330. // add modified classname to Fields
  331. if (field && uidl.hasAttribute("modified")) {
  332. styleBuf.append(" ");
  333. styleBuf.append(ApplicationConnection.MODIFIED_CLASSNAME);
  334. }
  335. // add error classname to components w/ error
  336. if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
  337. styleBuf.append(" ");
  338. styleBuf.append(primaryStyleName);
  339. styleBuf.append(ApplicationConnection.ERROR_CLASSNAME_EXT);
  340. }
  341. // add required style to required components
  342. if (uidl.hasAttribute(ATTRIBUTE_REQUIRED)) {
  343. styleBuf.append(" ");
  344. styleBuf.append(primaryStyleName);
  345. styleBuf.append(ApplicationConnection.REQUIRED_CLASSNAME_EXT);
  346. }
  347. return styleBuf.toString();
  348. }
  349. /*
  350. * (non-Javadoc)
  351. *
  352. * @see com.vaadin.terminal.gwt.client.ComponentConnector#isReadOnly()
  353. */
  354. @Deprecated
  355. public boolean isReadOnly() {
  356. return getState().isReadOnly();
  357. }
  358. /**
  359. * Sets the shared state for the paintable widget.
  360. *
  361. * @param new shared state (must be compatible with the return value of
  362. * {@link #getState()} - {@link ComponentState} if
  363. * {@link #getState()} is not overridden
  364. */
  365. public final void setState(SharedState state) {
  366. this.state = (ComponentState) state;
  367. }
  368. /**
  369. * Initialize the given RPC proxy object so it is connected to this
  370. * paintable.
  371. *
  372. * @param clientToServerRpc
  373. * The RPC instance to initialize. Must have been created using
  374. * GWT.create().
  375. */
  376. protected <T extends ServerRpc> T initRPC(T clientToServerRpc) {
  377. ((InitializableClientToServerRpc) clientToServerRpc).initRpc(
  378. getConnectorId(), getConnection());
  379. return clientToServerRpc;
  380. }
  381. public LayoutManager getLayoutManager() {
  382. return LayoutManager.get(getConnection());
  383. }
  384. /*
  385. * (non-Javadoc)
  386. *
  387. * @see com.vaadin.terminal.gwt.client.Connector#getParent()
  388. */
  389. public ComponentContainerConnector getParent() {
  390. return parent;
  391. }
  392. /*
  393. * (non-Javadoc)
  394. *
  395. * @see
  396. * com.vaadin.terminal.gwt.client.Connector#setParent(com.vaadin.terminal
  397. * .gwt.client.ComponentContainerConnector)
  398. */
  399. public void setParent(ComponentContainerConnector parent) {
  400. this.parent = parent;
  401. }
  402. }