1 package org.vaadin.aceeditor.client;
2
3 import java.util.Map;
4 import java.util.Map.Entry;
5
6 import org.vaadin.aceeditor.AceEditor;
7 import org.vaadin.aceeditor.client.AceEditorWidget.FocusChangeListener;
8 import org.vaadin.aceeditor.client.AceEditorWidget.SelectionChangeListener;
9 import org.vaadin.aceeditor.client.AceEditorWidget.TextChangeListener;
10 import org.vaadin.aceeditor.client.gwt.GwtAceEditor;
11
12 import com.google.gwt.core.client.GWT;
13 import com.google.gwt.user.client.Timer;
14 import com.google.gwt.user.client.ui.Widget;
15 import com.vaadin.client.ComponentConnector;
16 import com.vaadin.client.ConnectorHierarchyChangeEvent;
17 import com.vaadin.client.communication.RpcProxy;
18 import com.vaadin.client.communication.StateChangeEvent;
19 import com.vaadin.client.ui.AbstractHasComponentsConnector;
20 import com.vaadin.client.ui.layout.ElementResizeEvent;
21 import com.vaadin.client.ui.layout.ElementResizeListener;
22 import com.vaadin.shared.ui.Connect;
23
24 @SuppressWarnings("serial")
25 @Connect(AceEditor.class)
26 public class AceEditorConnector extends AbstractHasComponentsConnector
27 implements TextChangeListener, SelectionChangeListener, FocusChangeListener {
28
29
30
31 protected AceEditorServerRpc serverRpc =
32 RpcProxy.create(AceEditorServerRpc.class, this);
33
34
35 protected enum TextChangeEventMode {
36 EAGER, TIMEOUT, LAZY
37 }
38
39 protected TextChangeEventMode changeMode = null;
40 protected int changeTimeout = 400;
41
42 protected class SendTimer extends Timer {
43 private boolean scheduled;
44 private SendCond send = SendCond.NO;
45
46 public void schedule(int ms, SendCond send) {
47 super.schedule(ms);
48 this.send = this.send.or(send);
49 scheduled = true;
50 }
51
52 public void scheduleIfNotAlready(int ms, SendCond send) {
53 if (!scheduled) {
54 schedule(ms,send);
55 }
56 }
57
58 @Override
59 public void run() {
60 scheduled = false;
61 sendToServerImmediately(send);
62 send = SendCond.NO;
63 }
64
65 @Override
66 public void cancel() {
67 super.cancel();
68 send = SendCond.NO;
69 }
70 }
71
72 protected SendTimer sendTimer = null;
73
74 protected AceDoc shadow;
75
76 protected boolean onRoundtrip = false;
77
78 protected enum SendCond {
79 NO, IF_CHANGED, ALWAYS;
80 public SendCond or(SendCond sw2) {
81 return this.ordinal() > sw2.ordinal() ? this : sw2;
82 }
83 }
84
85 protected SendCond sendAfterRoundtrip = SendCond.NO;
86
87 protected AceEditorClientRpcRpc.html#AceEditorClientRpc">AceEditorClientRpc clientRpc = new AceEditorClientRpc() {
88 @Override
89 public void diff(TransportDiff ad) {
90 ClientSideDocDiff diff = ClientSideDocDiff.fromTransportDiff(ad);
91 shadow = diff.applyTo(shadow);
92
93 AceDoc doc1 = getWidget().getDoc();
94 AceDoc doc2 = diff.applyTo(doc1);
95
96 getWidget().setDoc(doc2);
97
98 if (selectionAfterApplyingDiff!=null) {
99 getWidget().setSelection(selectionAfterApplyingDiff);
100 selectionAfterApplyingDiff = null;
101 }
102
103 if (scrollToRowAfterApplyingDiff != -1) {
104 getWidget().scrollToRow(scrollToRowAfterApplyingDiff);
105 scrollToRowAfterApplyingDiff = -1;
106 }
107
108 setOnRoundtrip(false);
109 }
110
111 @Override
112 public void changedOnServer() {
113 if (!isOnRoundtrip()) {
114 sendToServer(SendCond.ALWAYS, true);
115 }
116
117 }
118
119 };
120
121 protected boolean listenToSelectionChanges;
122 protected boolean listenToFocusChanges;
123
124
125
126
127
128
129
130 protected int scrollToRowAfterApplyingDiff = -1;
131 protected AceRange selectionAfterApplyingDiff;
132
133 public AceEditorConnector() {
134 super();
135 registerRpc(AceEditorClientRpc.class, clientRpc);
136 }
137
138
139
140 @Override
141 public void init() {
142 super.init();
143
144
145
146 getLayoutManager().addElementResizeListener(getWidget().getElement(), new ElementResizeListener() {
147 @Override
148 public void onElementResize(ElementResizeEvent e) {
149 getWidget().resize();
150 }
151 });
152 }
153
154 @Override
155 public void onStateChanged(StateChangeEvent stateChangeEvent) {
156 super.onStateChanged(stateChangeEvent);
157
158 setTextChangeEventMode(getState().changeMode);
159 setTextChangeTimeout(getState().changeTimeout);
160
161 ClientSideDocDiff.dmp.setDiff_EditCost(getState().diff_editCost);
162
163
164
165
166
167
168 boolean firstTime = !getWidget().isInitialized();
169 if (firstTime) {
170
171
172
173 applyConfig(getState().config);
174 getWidget().initialize();
175 }
176
177 getWidget().setMode(getState().mode);
178 getWidget().setTheme(getState().theme);
179 listenToSelectionChanges = getState().listenToSelectionChanges;
180 listenToFocusChanges = getState().listenToFocusChanges;
181 getWidget().setUseWorker(getState().useWorker);
182 getWidget().setWordwrap(getState().wordwrap);
183
184 getWidget().setShowGutter(getState().showGutter);
185 getWidget().setShowPrintMargin(getState().showPrintMargin);
186 getWidget().setHighlightActiveLineEnabled(getState().highlightActiveLine);
187
188 getWidget().setPropertyReadOnly(getState().propertyReadOnly);
189 getWidget().setTabIndex(getState().tabIndex);
190 getWidget().setReadOnly(getState().readOnly);
191
192
193
194
195 if (firstTime) {
196 shadow = AceDoc.fromTransport(getState().initialValue);
197 getWidget().setDoc(shadow);
198 }
199
200 if (getState().selection != null) {
201 AceRange sel = AceRange.fromTransport(getState().selection);
202 if (firstTime) {
203 getWidget().setSelection(sel);
204 }
205 else {
206 selectionAfterApplyingDiff = sel;
207 }
208 }
209
210 if (getState().scrollToRow != -1) {
211 if (firstTime) {
212 getWidget().scrollToRow(getState().scrollToRow);
213 }
214 else {
215 scrollToRowAfterApplyingDiff = getState().scrollToRow;
216 }
217 }
218 }
219
220 protected static void applyConfig(Map<String, String> config) {
221 for (Entry<String, String> e : config.entrySet()) {
222 GwtAceEditor.setAceConfig(e.getKey(), e.getValue());
223 }
224 }
225
226 @Override
227 protected Widget createWidget() {
228 AceEditorWidget widget = GWT.create(AceEditorWidget.class);
229 widget.addTextChangeListener(this);
230 widget.addSelectionChangeListener(this);
231 widget.setFocusChangeListener(this);
232 return widget;
233 }
234
235 @Override
236 public AceEditorWidget getWidget() {
237 return (AceEditorWidget) super.getWidget();
238 }
239
240 @Override
241 public AceEditorState getState() {
242 return (AceEditorState) super.getState();
243 }
244
245 @Override
246 public void focusChanged(boolean focused) {
247
248
249 if (!listenToFocusChanges) {
250 return;
251 }
252
253 if (isOnRoundtrip()) {
254 sendAfterRoundtrip = sendAfterRoundtrip.or(SendCond.ALWAYS);
255 }
256 else {
257 sendToServerImmediately(SendCond.ALWAYS);
258 }
259 }
260
261 public void setTextChangeEventMode(String mode) {
262 TextChangeEventMode newMode = TextChangeEventMode.valueOf(mode);
263 if (newMode!=changeMode) {
264 changeTextChangeEventMode(newMode);
265 }
266 }
267
268 protected void setTextChangeTimeout(int timeout) {
269 changeTimeout = timeout;
270 }
271
272 protected void changeTextChangeEventMode(TextChangeEventMode newMode) {
273 if (sendTimer != null) {
274 sendTimer.cancel();
275 sendTimer = null;
276 }
277 this.changeMode = newMode;
278 }
279
280 protected void sendChangeAccordingToMode(SendCond send) {
281 sendChangeAccordingToMode(send, changeMode);
282 }
283
284 protected void sendChangeAccordingToMode(SendCond send, TextChangeEventMode mode) {
285 if (mode == TextChangeEventMode.EAGER) {
286 if (sendTimer != null) {
287 sendTimer.cancel();
288 }
289 sendToServerImmediately(send);
290 } else if (mode == TextChangeEventMode.LAZY) {
291 if (sendTimer == null) {
292 sendTimer = new SendTimer();
293 }
294 sendTimer.schedule(changeTimeout, send);
295 } else if (mode == TextChangeEventMode.TIMEOUT) {
296 if (sendTimer == null) {
297 sendTimer = new SendTimer();
298 }
299 sendTimer.scheduleIfNotAlready(changeTimeout, send);
300 }
301 }
302
303 protected void sendToServer(SendCond send, boolean immediately) {
304 if (send==SendCond.NO) {
305 return;
306 }
307
308 AceDoc doc = getWidget().getDoc();
309 ClientSideDocDiff diff = ClientSideDocDiff.diff(shadow, doc);
310 if (send==SendCond.ALWAYS) {
311
312 }
313 else if (send==SendCond.IF_CHANGED && !diff.isIdentity()) {
314
315 }
316 else {
317 return;
318 }
319
320 TransportDiff td = diff.asTransport();
321
322 if (immediately) {
323 serverRpc.changed(td, getWidget().getSelection().asTransport(), getWidget().isFocused());
324 } else {
325 serverRpc.changedDelayed(td, getWidget().getSelection().asTransport(), getWidget().isFocused());
326 }
327
328 shadow = doc;
329 setOnRoundtrip(true);
330 sendAfterRoundtrip = SendCond.NO;
331 }
332
333 protected void sendToServerDelayed(SendCond send) {
334 sendToServer(send, false);
335 }
336
337 public void sendToServerImmediately() {
338 sendToServerImmediately(SendCond.ALWAYS);
339 }
340
341 protected void sendToServerImmediately(SendCond send) {
342 sendToServer(send, true);
343 }
344
345 @Override
346 public void flush() {
347 super.flush();
348 sendWhenPossible(SendCond.ALWAYS, TextChangeEventMode.EAGER);
349 }
350
351 @Override
352 public void changed() {
353 if (isOnRoundtrip()) {
354 sendAfterRoundtrip = sendAfterRoundtrip.or(SendCond.IF_CHANGED);
355 }
356 else {
357 sendChangeAccordingToMode(SendCond.IF_CHANGED);
358 }
359 }
360
361 @Override
362 public void updateCaption(ComponentConnector connector) {
363
364
365 }
366
367 @Override
368 public void onConnectorHierarchyChange(
369 ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) {
370
371
372 }
373
374 @Override
375 public void selectionChanged() {
376
377
378 if (listenToSelectionChanges) {
379 sendWhenPossible(SendCond.ALWAYS);
380 }
381 }
382
383 protected void sendWhenPossible(SendCond send) {
384 if (isOnRoundtrip()) {
385 sendAfterRoundtrip = sendAfterRoundtrip.or(send);
386 }
387 else {
388 sendChangeAccordingToMode(send);
389 }
390 }
391
392 protected void sendWhenPossible(SendCond send, TextChangeEventMode mode) {
393 if (isOnRoundtrip()) {
394 sendAfterRoundtrip = sendAfterRoundtrip.or(send);
395 }
396 else {
397 sendChangeAccordingToMode(send, mode);
398 }
399 }
400
401
402 private void setOnRoundtrip(boolean on) {
403 if (on==onRoundtrip) {
404 return;
405 }
406 onRoundtrip = on;
407 if (!onRoundtrip) {
408 sendToServerImmediately(sendAfterRoundtrip);
409 }
410 }
411
412 public boolean isOnRoundtrip() {
413 return onRoundtrip;
414 }
415 }