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

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