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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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.ClientToServerRpc;
  20. import com.vaadin.terminal.gwt.client.communication.ClientToServerRpc.InitializableClientToServerRpc;
  21. import com.vaadin.terminal.gwt.client.communication.SharedState;
  22. public abstract class AbstractComponentConnector extends AbstractConnector
  23. implements ComponentConnector {
  24. // Generic UIDL parameter names, to be moved to shared state.
  25. // Attributes are here mainly if they apply to all paintable widgets or
  26. // affect captions - otherwise, they are in the relevant subclasses.
  27. // For e.g. item or context specific attributes, subclasses may use separate
  28. // constants, which may refer to these.
  29. // Not all references to the string literals have been converted to use
  30. // these!
  31. public static final String ATTRIBUTE_ICON = "icon";
  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. public ComponentContainerConnector getParent() {
  99. // FIXME: Hierarchy should be set by framework instead of looked up here
  100. ConnectorMap paintableMap = ConnectorMap.get(getConnection());
  101. Widget w = getWidget();
  102. while (true) {
  103. w = w.getParent();
  104. if (w == null) {
  105. return null;
  106. }
  107. if (paintableMap.isConnector(w)) {
  108. return (ComponentContainerConnector) paintableMap
  109. .getConnector(w);
  110. }
  111. }
  112. }
  113. protected static boolean isRealUpdate(UIDL uidl) {
  114. return !isCachedUpdate(uidl) && !uidl.getBooleanAttribute("invisible")
  115. && !uidl.hasAttribute("deferred");
  116. }
  117. protected static boolean isCachedUpdate(UIDL uidl) {
  118. return uidl.getBooleanAttribute("cached");
  119. }
  120. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  121. if (isCachedUpdate(uidl)) {
  122. return;
  123. }
  124. ConnectorMap paintableMap = ConnectorMap.get(getConnection());
  125. // register the listened events by the server-side to the event-handler
  126. // of the component
  127. paintableMap.registerEventListenersFromUIDL(getId(), uidl);
  128. // Visibility
  129. setVisible(!uidl.getBooleanAttribute("invisible"), uidl);
  130. if (uidl.getId().startsWith("PID_S")) {
  131. DOM.setElementProperty(getWidget().getElement(), "id", uidl.getId()
  132. .substring(5));
  133. }
  134. if (!isVisible()) {
  135. // component is invisible, delete old size to notify parent, if
  136. // later made visible
  137. paintableMap.setOffsetSize(this, null);
  138. return;
  139. }
  140. if (!isRealUpdate(uidl)) {
  141. return;
  142. }
  143. /*
  144. * Disabled state may affect (override) tabindex so the order must be
  145. * first setting tabindex, then enabled state.
  146. */
  147. if (uidl.hasAttribute("tabindex") && getWidget() instanceof Focusable) {
  148. ((Focusable) getWidget()).setTabIndex(uidl
  149. .getIntAttribute("tabindex"));
  150. }
  151. setEnabled(!getState().isDisabled());
  152. // Style names
  153. String styleName = getStyleNameFromUIDL(getWidget()
  154. .getStylePrimaryName(), uidl, getState(),
  155. getWidget() instanceof Field);
  156. getWidget().setStyleName(styleName);
  157. // Update tooltip
  158. TooltipInfo tooltipInfo = paintableMap.getTooltipInfo(this, null);
  159. if (getState().hasDescription()) {
  160. tooltipInfo.setTitle(getState().getDescription());
  161. } else {
  162. tooltipInfo.setTitle(null);
  163. }
  164. // add error info to tooltip if present
  165. if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
  166. tooltipInfo.setErrorUidl(uidl.getErrors());
  167. } else {
  168. tooltipInfo.setErrorUidl(null);
  169. }
  170. // Set captions
  171. if (delegateCaptionHandling()) {
  172. getParent().updateCaption(this, uidl);
  173. }
  174. /*
  175. * updateComponentSize need to be after caption update so caption can be
  176. * taken into account
  177. */
  178. updateComponentSize();
  179. }
  180. private void updateComponentSize() {
  181. SharedState state = getState();
  182. String w = "";
  183. String h = "";
  184. if (null != state || !(state instanceof ComponentState)) {
  185. ComponentState componentState = (ComponentState) state;
  186. // TODO move logging to VUIDLBrowser and VDebugConsole
  187. // VConsole.log("Paintable state for "
  188. // + getPaintableMap().getPid(paintable) + ": "
  189. // + String.valueOf(state.getState()));
  190. h = componentState.getHeight();
  191. w = componentState.getWidth();
  192. } else {
  193. // TODO move logging to VUIDLBrowser and VDebugConsole
  194. VConsole.log("No state for paintable " + getId()
  195. + " in VAbstractPaintableWidget.updateComponentSize()");
  196. }
  197. // Parent should be updated if either dimension changed between relative
  198. // and non-relative
  199. if (w.endsWith("%") != declaredWidth.endsWith("%")) {
  200. ComponentContainerConnector parent = getParent();
  201. if (parent instanceof ManagedLayout) {
  202. getLayoutManager().setWidthNeedsUpdate((ManagedLayout) parent);
  203. }
  204. }
  205. if (h.endsWith("%") != declaredHeight.endsWith("%")) {
  206. ComponentContainerConnector parent = getParent();
  207. if (parent instanceof ManagedLayout) {
  208. getLayoutManager().setHeightNeedsUpdate((ManagedLayout) parent);
  209. }
  210. }
  211. declaredWidth = w;
  212. declaredHeight = h;
  213. // Set defined sizes
  214. Widget component = getWidget();
  215. component.setStyleName("v-undefined-width", isUndefinedWidth());
  216. component.setStyleName("v-undefined-height", isUndefinedHeight());
  217. component.setHeight(h);
  218. component.setWidth(w);
  219. }
  220. public boolean isRelativeHeight() {
  221. return declaredHeight.endsWith("%");
  222. }
  223. public boolean isRelativeWidth() {
  224. return declaredWidth.endsWith("%");
  225. }
  226. public boolean isUndefinedHeight() {
  227. return declaredHeight.length() == 0;
  228. }
  229. public boolean isUndefinedWidth() {
  230. return declaredWidth.length() == 0;
  231. }
  232. public String getDeclaredHeight() {
  233. return declaredHeight;
  234. }
  235. public String getDeclaredWidth() {
  236. return declaredWidth;
  237. }
  238. /**
  239. * Sets the enabled state of this paintable
  240. *
  241. * @param enabled
  242. * true if the paintable is enabled, false otherwise
  243. */
  244. protected void setEnabled(boolean enabled) {
  245. this.enabled = enabled;
  246. if (getWidget() instanceof FocusWidget) {
  247. FocusWidget fw = (FocusWidget) getWidget();
  248. fw.setEnabled(enabled);
  249. }
  250. }
  251. public boolean isEnabled() {
  252. return enabled;
  253. }
  254. /**
  255. * Return true if parent handles caption, false if the paintable handles the
  256. * caption itself.
  257. *
  258. *
  259. * @deprecated This should always return true and all components should let
  260. * the parent handle the caption and use other attributes for
  261. * internal texts in the component
  262. * @return
  263. */
  264. @Deprecated
  265. protected boolean delegateCaptionHandling() {
  266. return true;
  267. }
  268. /**
  269. * Sets the visible state for this paintable.
  270. *
  271. * @param visible
  272. * true if the paintable should be made visible, false otherwise
  273. * @param captionUidl
  274. * The UIDL that is passed to the parent and onwards to VCaption
  275. * if the caption needs to be updated as a result of the
  276. * visibility change.
  277. */
  278. protected void setVisible(boolean visible, UIDL captionUidl) {
  279. boolean wasVisible = this.visible;
  280. this.visible = visible;
  281. getWidget().setVisible(visible);
  282. if (wasVisible != visible) {
  283. // Changed invisibile <-> visible
  284. if (wasVisible && delegateCaptionHandling()) {
  285. // Must hide caption when component is hidden
  286. getParent().updateCaption(this, captionUidl);
  287. }
  288. }
  289. }
  290. protected boolean isVisible() {
  291. return visible;
  292. }
  293. /**
  294. * Generates the style name for the widget based on the given primary style
  295. * name (typically returned by Widget.getPrimaryStyleName()) and the UIDL
  296. * and shared state of the component. An additional "modified" style name
  297. * can be added if the field parameter is set to true.
  298. *
  299. * @param primaryStyleName
  300. * @param uidl
  301. * @param state
  302. * component shared state
  303. * @param field
  304. * @return
  305. */
  306. protected static String getStyleNameFromUIDL(String primaryStyleName,
  307. UIDL uidl, ComponentState state, boolean field) {
  308. boolean enabled = !state.isDisabled();
  309. StringBuffer styleBuf = new StringBuffer();
  310. styleBuf.append(primaryStyleName);
  311. styleBuf.append(" v-paintable");
  312. // first disabling and read-only status
  313. if (!enabled) {
  314. styleBuf.append(" ");
  315. styleBuf.append(ApplicationConnection.DISABLED_CLASSNAME);
  316. }
  317. if (state.isReadOnly()) {
  318. styleBuf.append(" ");
  319. styleBuf.append("v-readonly");
  320. }
  321. // add additional styles as css classes, prefixed with component default
  322. // stylename
  323. if (state.hasStyles()) {
  324. final String[] styles = state.getStyle().split(" ");
  325. for (int i = 0; i < styles.length; i++) {
  326. styleBuf.append(" ");
  327. styleBuf.append(primaryStyleName);
  328. styleBuf.append("-");
  329. styleBuf.append(styles[i]);
  330. styleBuf.append(" ");
  331. styleBuf.append(styles[i]);
  332. }
  333. }
  334. // add modified classname to Fields
  335. if (field && uidl.hasAttribute("modified")) {
  336. styleBuf.append(" ");
  337. styleBuf.append(ApplicationConnection.MODIFIED_CLASSNAME);
  338. }
  339. // add error classname to components w/ error
  340. if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
  341. styleBuf.append(" ");
  342. styleBuf.append(primaryStyleName);
  343. styleBuf.append(ApplicationConnection.ERROR_CLASSNAME_EXT);
  344. }
  345. // add required style to required components
  346. if (uidl.hasAttribute(ATTRIBUTE_REQUIRED)) {
  347. styleBuf.append(" ");
  348. styleBuf.append(primaryStyleName);
  349. styleBuf.append(ApplicationConnection.REQUIRED_CLASSNAME_EXT);
  350. }
  351. return styleBuf.toString();
  352. }
  353. /**
  354. * Sets the shared state for the paintable widget.
  355. *
  356. * @param new shared state (must be compatible with the return value of
  357. * {@link #getState()} - {@link ComponentState} if
  358. * {@link #getState()} is not overridden
  359. */
  360. public final void setState(SharedState state) {
  361. this.state = (ComponentState) state;
  362. }
  363. /**
  364. * Initialize the given RPC proxy object so it is connected to this
  365. * paintable.
  366. *
  367. * @param clientToServerRpc
  368. * The RPC instance to initialize. Must have been created using
  369. * GWT.create().
  370. */
  371. protected <T extends ClientToServerRpc> T initRPC(T clientToServerRpc) {
  372. ((InitializableClientToServerRpc) clientToServerRpc).initRpc(getId(),
  373. getConnection());
  374. return clientToServerRpc;
  375. }
  376. public LayoutManager getLayoutManager() {
  377. return LayoutManager.get(getConnection());
  378. }
  379. }