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

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