View Javadoc
1   package org.vaadin.hene.expandingtextarea;
2   
3   import java.io.Serializable;
4   import java.lang.reflect.Method;
5   
6   import org.vaadin.hene.expandingtextarea.widgetset.client.ui.ExpandingTextAreaServerRpc;
7   
8   import com.vaadin.v7.data.Property;
9   import com.vaadin.server.PaintException;
10  import com.vaadin.server.PaintTarget;
11  import com.vaadin.v7.ui.TextArea;
12  import com.vaadin.util.ReflectTools;
13  
14  
15  /**
16   * An extended version of {@link TextArea} that adapts its height to the
17   * textual content.
18   *
19   * @author Henri Kerola / Vaadin
20   */
21  @SuppressWarnings("serial")
22  public class ExpandingTextArea extends TextArea {
23  
24  	private static final Method ROWS_CHANGE_METHOD = ReflectTools.findMethod(
25  			RowsChangeListener.class, "rowsChange", RowsChangeEvent.class);
26  
27  	private int rows = 2;
28  	private Integer maxRows = null;
29  	
30  	private ExpandingTextAreaServerRpclient/ui/ExpandingTextAreaServerRpc.html#ExpandingTextAreaServerRpc">ExpandingTextAreaServerRpc rpc = new ExpandingTextAreaServerRpc() {
31  		public void setRows(int rows) {
32  			ExpandingTextArea.this.rows = rows;
33  			fireRowsChangeEvent();
34  		}
35  	};
36  
37  	/**
38  	 * Constructs an empty <code>ExpandingTextArea</code> with no caption.
39  	 */
40  	public ExpandingTextArea() {
41  		registerRpc(rpc);
42  		setRows(2);
43  		setValue("");
44  	}
45  
46  	/**
47  	 * Constructs an empty <code>ExpandingTextArea</code> with given caption.
48  	 * 
49  	 * @param caption
50  	 *            the caption <code>String</code> for the editor.
51  	 */
52  	public ExpandingTextArea(String caption) {
53  		registerRpc(rpc);
54  		setRows(2);
55  		setCaption(caption);
56  	}
57  
58  	/**
59  	 * Constructs a new <code>ExpandingTextArea</code> that's bound to the
60  	 * specified <code>Property</code> and has no caption.
61  	 * 
62  	 * @param dataSource
63  	 *            the Property to be edited with this editor.
64  	 */
65  	public ExpandingTextArea(Property dataSource) {
66  		registerRpc(rpc);
67  		setRows(2);
68  		setPropertyDataSource(dataSource);
69  	}
70  
71  	/**
72  	 * Constructs a new <code>ExpandingTextArea</code> that's bound to the
73  	 * specified <code>Property</code> and has the given caption
74  	 * <code>String</code>.
75  	 * 
76  	 * @param caption
77  	 *            the caption <code>String</code> for the editor.
78  	 * @param dataSource
79  	 *            the Property to be edited with this editor.
80  	 */
81  	public ExpandingTextArea(String caption, Property dataSource) {
82  		this(dataSource);
83  		setCaption(caption);
84  	}
85  
86  	/**
87  	 * Constructs a new <code>ExpandingTextArea</code> with the given caption
88  	 * and initial text contents. The editor constructed this way will not be
89  	 * bound to a Property unless
90  	 * {@link com.vaadin.data.Property.Viewer#setPropertyDataSource(Property)}
91  	 * is called to bind it.
92  	 * 
93  	 * @param caption
94  	 *            the caption <code>String</code> for the editor.
95  	 * @param value
96  	 *            the initial text content of the editor.
97  	 */
98  	public ExpandingTextArea(String caption, String value) {
99  		registerRpc(rpc);
100 		setRows(2);
101 		setValue(value);
102 		setCaption(caption);
103 	}
104 
105 	@Override
106 	public void paintContent(PaintTarget target) throws PaintException {
107 		target.addVariable(this, "rows", getRows());
108 		if (maxRows != null) {
109 			target.addAttribute("maxRows", maxRows);
110 		}
111 		super.paintContent(target);
112 	}
113 
114 	@Override
115 	public void setRows(int rows) {
116 		if (rows < 2) {
117 			throw new IllegalArgumentException("rows must be >= 2");
118 		}
119 		if (this.rows != rows) {
120 			this.rows = rows;
121 			super.setRows(rows);
122 			fireRowsChangeEvent();
123 		}
124 	}
125 
126 	@Override
127 	public int getRows() {
128 		return rows;
129 	}
130 
131 	@Override
132 	public void setHeight(float height, Unit unit) {
133 		if (height == -1) {
134 			super.setHeight(height, unit);
135 		} else {
136 			throw new IllegalArgumentException(
137 					"ExpandingTextArea supports only undefined height");
138 		}
139 	}
140 
141 	/**
142 	 * Sets the maximum allowed number of rows that the {@link ExpandingTextArea} will grow to,
143 	 * default is null that means that there is no limit for growing.
144 	 *
145 	 * Please note that max rows doesn't work very well with IE8.
146 	 *
147 	 * @param maxRows null or >= 2.
148 	 */
149 	public void setMaxRows(Integer maxRows) {
150 		if (maxRows != null && maxRows < 2) {
151 			throw new IllegalArgumentException("maxRows must be >= 2");
152 		}
153 		if ((this.maxRows == null && maxRows != null)
154 				|| !this.maxRows.equals(maxRows)) {
155 			this.maxRows = maxRows;
156 			requestRepaint();
157 		}
158 	}
159 
160 	/**
161 	 * Returns the maximum allowed number of rows that the {@link ExpandingTextArea} will grow to,
162 	 * default is null that means that there is no limit for growing.
163 	 *
164 	 * Please note that max rows doesn't work very well with IE8.
165 	 */
166 	public int getMaxRows() {
167 		return maxRows;
168 	}
169 
170 	/**
171 	 * Adds a {@link RowsChangeListener} to the component.
172 	 */
173 	public void addRowsChangeListener(RowsChangeListener listener) {
174 		addListener(RowsChangeEvent.class, listener, ROWS_CHANGE_METHOD);
175 	}
176 
177 	/**
178 	 * Removes a {@link RowsChangeListener} from the component.
179 	 */
180 	public void removeRowsChangeListener(RowsChangeListener listener) {
181 		removeListener(RowsChangeEvent.class, listener, ROWS_CHANGE_METHOD);
182 	}
183 
184 	private void fireRowsChangeEvent() {
185 		fireEvent(new RowsChangeEvent(this));
186 	}
187 
188 	public class RowsChangeEvent extends Event {
189 
190 		public RowsChangeEvent(ExpandingTextArea source) {
191 			super(source);
192 		}
193 
194 		public ExpandingTextArea getExpandingTextArea() {
195 			return (ExpandingTextArea) getSource();
196 		}
197 
198 		public int getRows() {
199 			return getExpandingTextArea().getRows();
200 		}
201 	}
202 
203 	/**
204 	 * Listener for row count changes. When text in {@link ExpandingTextArea}
205 	 * is modified (by user or programmatically) and the modification affects
206 	 * to row count, then possible {@link RowsChangeListener} added to the
207 	 * component are called.
208 	 *
209 	 * Please note, that for a disabled {@link ExpandingTextArea} listeners
210 	 * won't be called. This is because Vaadin doesn't allow a disabled
211 	 * component to call a RPC from client to server.
212 	 */
213 	public interface RowsChangeListener extends Serializable {
214 
215 		void rowsChange(RowsChangeEvent event);
216 	}
217 
218 }