1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_ADD;
18 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_BACK_SPACE;
19 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_DECIMAL;
20 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_DELETE;
21 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_DIVIDE;
22 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_END;
23 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_EQUALS;
24 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_HOME;
25 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_LEFT;
26 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_MULTIPLY;
27 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_NUMPAD0;
28 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_NUMPAD9;
29 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_RIGHT;
30 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_SEMICOLON;
31 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_SEPARATOR;
32 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_SPACE;
33 import static org.htmlunit.javascript.host.event.KeyboardEvent.DOM_VK_SUBTRACT;
34
35 import java.io.Serializable;
36 import java.util.HashMap;
37 import java.util.Map;
38
39 import org.htmlunit.ClipboardHandler;
40 import org.htmlunit.html.impl.SelectionDelegate;
41
42
43
44
45
46
47
48
49
50 class DoTypeProcessor implements Serializable {
51
52 private static final Map<Integer, Character> SPECIAL_KEYS_MAP_ = new HashMap<>();
53
54
55
56
57 private final DomNode domNode_;
58
59 static {
60 SPECIAL_KEYS_MAP_.put(DOM_VK_ADD, '+');
61 SPECIAL_KEYS_MAP_.put(DOM_VK_DECIMAL, '.');
62 SPECIAL_KEYS_MAP_.put(DOM_VK_DIVIDE, '/');
63 SPECIAL_KEYS_MAP_.put(DOM_VK_EQUALS, '=');
64 SPECIAL_KEYS_MAP_.put(DOM_VK_MULTIPLY, '*');
65 SPECIAL_KEYS_MAP_.put(DOM_VK_SEMICOLON, ';');
66 SPECIAL_KEYS_MAP_.put(DOM_VK_SEPARATOR, ',');
67 SPECIAL_KEYS_MAP_.put(DOM_VK_SPACE, ' ');
68 SPECIAL_KEYS_MAP_.put(DOM_VK_SUBTRACT, '-');
69
70 for (int i = DOM_VK_NUMPAD0; i <= DOM_VK_NUMPAD9; i++) {
71 SPECIAL_KEYS_MAP_.put(i, (char) ('0' + i - DOM_VK_NUMPAD0));
72 }
73 }
74
75 DoTypeProcessor(final DomNode domNode) {
76 domNode_ = domNode;
77 }
78
79 void doType(final String currentValue, final SelectionDelegate selectionDelegate,
80 final char c, final HtmlElement element, final boolean lastType) {
81
82 int selectionStart = selectionDelegate.getSelectionStart();
83 selectionStart = Math.max(0, Math.min(selectionStart, currentValue.length()));
84
85 int selectionEnd = selectionDelegate.getSelectionEnd();
86 selectionEnd = Math.max(selectionStart, Math.min(selectionEnd, currentValue.length()));
87
88 final StringBuilder newValue = new StringBuilder(currentValue);
89 if (c == '\b') {
90 if (selectionStart > 0) {
91 newValue.deleteCharAt(selectionStart - 1);
92 selectionStart--;
93 selectionEnd--;
94 }
95 }
96 else if (acceptChar(c)) {
97 final boolean ctrlKey = element.isCtrlPressed();
98 if (ctrlKey && (c == 'C' || c == 'c')) {
99 final ClipboardHandler clipboardHandler = element.getPage().getWebClient().getClipboardHandler();
100 if (clipboardHandler != null) {
101 final String content = newValue.substring(selectionStart, selectionEnd);
102 clipboardHandler.setClipboardContent(content);
103 }
104 }
105 else if (ctrlKey && (c == 'V' || c == 'v')) {
106 final ClipboardHandler clipboardHandler = element.getPage().getWebClient().getClipboardHandler();
107 if (clipboardHandler != null) {
108 final String content = clipboardHandler.getClipboardContent();
109 add(newValue, content, selectionStart, selectionEnd);
110 selectionStart += content.length();
111 selectionEnd = selectionStart;
112 }
113 }
114 else if (ctrlKey && (c == 'X' || c == 'x')) {
115 final ClipboardHandler clipboardHandler = element.getPage().getWebClient().getClipboardHandler();
116 if (clipboardHandler != null) {
117 final String content = newValue.substring(selectionStart, selectionEnd);
118 clipboardHandler.setClipboardContent(content);
119 newValue.delete(selectionStart, selectionEnd);
120 selectionEnd = selectionStart;
121 }
122 }
123 else if (ctrlKey && (c == 'A' || c == 'a')) {
124 selectionStart = 0;
125 selectionEnd = newValue.length();
126 }
127 else {
128 add(newValue, c, selectionStart, selectionEnd);
129 selectionStart++;
130 selectionEnd = selectionStart;
131 }
132 }
133
134 typeDone(newValue.toString(), lastType);
135
136 selectionDelegate.setSelectionStart(selectionStart);
137 selectionDelegate.setSelectionEnd(selectionEnd);
138 }
139
140 private static void add(final StringBuilder newValue, final char c, final int selectionStart,
141 final int selectionEnd) {
142 if (selectionStart == newValue.length()) {
143 newValue.append(c);
144 }
145 else {
146 newValue.replace(selectionStart, selectionEnd, Character.toString(c));
147 }
148 }
149
150 private static void add(final StringBuilder newValue, final String string, final int selectionStart,
151 final int selectionEnd) {
152 if (selectionStart == newValue.length()) {
153 newValue.append(string);
154 }
155 else {
156 newValue.replace(selectionStart, selectionEnd, string);
157 }
158 }
159
160 private void typeDone(final String newValue, final boolean notifyAttributeChangeListeners) {
161 if (domNode_ instanceof DomText) {
162 ((DomText) domNode_).setData(newValue);
163 }
164 else {
165 ((HtmlElement) domNode_).typeDone(newValue, notifyAttributeChangeListeners);
166 }
167 }
168
169 private boolean acceptChar(final char ch) {
170 if (domNode_ instanceof DomText) {
171 return ((DomText) domNode_).acceptChar(ch);
172 }
173 return ((HtmlElement) domNode_).acceptChar(ch);
174 }
175
176 void doType(final String currentValue, final SelectionDelegate selectionDelegate,
177 final int keyCode, final HtmlElement element, final boolean lastType) {
178
179 int selectionStart = selectionDelegate.getSelectionStart();
180 selectionStart = Math.max(0, Math.min(selectionStart, currentValue.length()));
181
182 int selectionEnd = selectionDelegate.getSelectionEnd();
183 selectionEnd = Math.max(selectionStart, Math.min(selectionEnd, currentValue.length()));
184
185 final Character ch = SPECIAL_KEYS_MAP_.get(keyCode);
186 if (ch != null) {
187 doType(currentValue, selectionDelegate, ch, element, lastType);
188 return;
189 }
190
191 final StringBuilder newValue = new StringBuilder(currentValue);
192 switch (keyCode) {
193 case DOM_VK_BACK_SPACE:
194 if (selectionStart > 0) {
195 newValue.deleteCharAt(selectionStart - 1);
196 selectionStart--;
197 }
198 break;
199
200 case DOM_VK_LEFT:
201 if (element.isCtrlPressed()) {
202 while (selectionStart > 0 && newValue.charAt(selectionStart - 1) != ' ') {
203 selectionStart--;
204 }
205 }
206 else if (selectionStart > 0) {
207 selectionStart--;
208 }
209 break;
210
211 case DOM_VK_RIGHT:
212 if (element.isCtrlPressed()) {
213 if (selectionStart < newValue.length()) {
214 selectionStart++;
215 }
216 while (selectionStart < newValue.length() && newValue.charAt(selectionStart - 1) != ' ') {
217 selectionStart++;
218 }
219 }
220 else if (element.isShiftPressed()) {
221 selectionEnd++;
222 }
223 else if (selectionStart > 0) {
224 selectionStart++;
225 }
226 break;
227
228 case DOM_VK_HOME:
229 selectionStart = 0;
230 break;
231
232 case DOM_VK_END:
233 if (element.isShiftPressed()) {
234 selectionEnd = newValue.length();
235 }
236 else {
237 selectionStart = newValue.length();
238 }
239 break;
240
241 case DOM_VK_DELETE:
242 if (selectionEnd == selectionStart) {
243 selectionEnd++;
244 }
245 newValue.delete(selectionStart, selectionEnd);
246 selectionEnd = selectionStart;
247 break;
248
249 default:
250 return;
251 }
252
253 if (!element.isShiftPressed()) {
254 selectionEnd = selectionStart;
255 }
256
257 typeDone(newValue.toString(), lastType);
258
259 selectionDelegate.setSelectionStart(selectionStart);
260 selectionDelegate.setSelectionEnd(selectionEnd);
261 }
262 }