1 | /** |
2 | * Copyright 2005-2011 Steve McDuff d-duff@users.sourceforge.net |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.deduced.viewer.web.shared; |
17 | |
18 | import java.util.HashMap; |
19 | import java.util.Map.Entry; |
20 | import java.util.Set; |
21 | |
22 | import com.google.gwt.dom.client.Style; |
23 | import com.google.gwt.event.shared.HandlerRegistration; |
24 | import com.google.gwt.i18n.client.HasDirection.Direction; |
25 | import com.google.gwt.user.client.Element; |
26 | import com.google.gwt.user.client.ui.HasHorizontalAlignment; |
27 | import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant; |
28 | import com.google.gwt.user.client.ui.HasVerticalAlignment; |
29 | import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant; |
30 | import com.google.gwt.user.client.ui.UIObject; |
31 | import com.google.gwt.user.client.ui.ValueBoxBase.TextAlignment; |
32 | |
33 | /** |
34 | * |
35 | * UserInterfaceModel is the base class for all models that display UI elements. |
36 | * It defines the methods required to synchronize the UI with the model. The UI |
37 | * widget is stored in the uiObject field for all implementations. |
38 | * |
39 | * @author Steve McDuff |
40 | * |
41 | */ |
42 | public abstract class UserInterfaceModel extends Model |
43 | { |
44 | /** |
45 | * serialVersionUID |
46 | */ |
47 | private static final long serialVersionUID = -8395649857779214930L; |
48 | |
49 | /** |
50 | * flag used for tests to avoid creating GWT objects |
51 | */ |
52 | private static boolean GWT_OBJECT_CREATION_ENABLED = true; |
53 | |
54 | /** |
55 | * style of the user interface element |
56 | */ |
57 | private String style; |
58 | |
59 | /** |
60 | * The previously applied style |
61 | */ |
62 | private transient String previousStyle; |
63 | |
64 | /** |
65 | * name of the user interface element. |
66 | */ |
67 | private String name; |
68 | |
69 | /** |
70 | * is the user interface element visible |
71 | */ |
72 | private boolean isVisible = true; |
73 | |
74 | /** |
75 | * The UI object matching this model |
76 | */ |
77 | private transient UIObject uiObject; |
78 | |
79 | /** |
80 | * get the UI Object |
81 | * |
82 | * @return the UI object |
83 | */ |
84 | public UIObject getUIObject() |
85 | { |
86 | return uiObject; |
87 | } |
88 | |
89 | /** |
90 | * create the UI Object using the right widget |
91 | * |
92 | * @return the created UI widget |
93 | */ |
94 | public abstract UIObject createUIObject(); |
95 | |
96 | /** |
97 | * (non-JSDoc) |
98 | * |
99 | * @see org.deduced.viewer.web.shared.Model#registrationComplete() |
100 | */ |
101 | @Override |
102 | protected void registrationComplete() |
103 | { |
104 | initializeUIObject(); |
105 | } |
106 | |
107 | /** |
108 | * initialize UI Object |
109 | */ |
110 | public void initializeUIObject() |
111 | { |
112 | if (getUIObject() == null && isGwtObjectCreationEnabled()) |
113 | { |
114 | UIObject createUIObject = createUIObject(); |
115 | setUIObject(createUIObject); |
116 | registerHandlers(); |
117 | synchronizeWithUI(); |
118 | } |
119 | } |
120 | |
121 | /** |
122 | * override this method to register all the handlers required on the UI |
123 | * object |
124 | */ |
125 | protected void registerHandlers() |
126 | { |
127 | |
128 | } |
129 | |
130 | /** |
131 | * (non-JSDoc) |
132 | * |
133 | * @see org.deduced.viewer.web.shared.Model#propertyChanged(org.deduced.viewer.web.shared.ChangeEvent) |
134 | */ |
135 | @Override |
136 | public void propertyChanged( |
137 | ChangeEvent event) |
138 | { |
139 | if (Utilities.equals(event.getName(), "style")) |
140 | { |
141 | setStyle((String) event.getSerializableValue()); |
142 | synchronizeWithUI(); |
143 | } |
144 | else if (Utilities.equals(event.getName(), "is visible")) |
145 | { |
146 | setIsVisible(((Boolean) event.getSerializableValue()) |
147 | .booleanValue()); |
148 | synchronizeWithUI(); |
149 | } |
150 | else if (Utilities.equals(event.getName(), "name")) |
151 | { |
152 | setName((String) event.getSerializableValue()); |
153 | synchronizeWithUI(); |
154 | } |
155 | } |
156 | |
157 | /** |
158 | * (non-JSDoc) |
159 | * |
160 | * @see org.deduced.viewer.web.shared.Model#deleteChildModels() |
161 | */ |
162 | @Override |
163 | protected void deleteChildModels() |
164 | { |
165 | unregisterHandlers(); |
166 | setUIObject(null); |
167 | super.deleteChildModels(); |
168 | } |
169 | |
170 | /** |
171 | * unregister Handlers. Override this method to unregister any handlers that |
172 | * might be associated with the UI objects. |
173 | */ |
174 | protected void unregisterHandlers() |
175 | { |
176 | |
177 | } |
178 | |
179 | /** |
180 | * synchronize the model with the UI element. This method will call |
181 | * internalSynchronizeWithUI and report any caught error. |
182 | */ |
183 | public final void synchronizeWithUI() |
184 | { |
185 | ModelRegistry modelRegistry = getModelRegistry(); |
186 | modelRegistry.monitorSynchronizeWithUI(this); |
187 | |
188 | if (getUIObject() == null) |
189 | { |
190 | return; |
191 | } |
192 | |
193 | try |
194 | { |
195 | internalSynchronizeWithUI(); |
196 | } |
197 | catch (Throwable e) |
198 | { |
199 | modelRegistry.showErrorException("synchronizeWithUI error.", e); |
200 | } |
201 | } |
202 | |
203 | /** |
204 | * internal Synchronize With UI. It is recommended to override this method |
205 | * to synchronize model specific fields. The override must then call |
206 | * super.internalSynchronizeWithUI() to synchronize all the fields that it |
207 | * inherits. |
208 | */ |
209 | public void internalSynchronizeWithUI() |
210 | { |
211 | UIObject currentUIObject = getUIObject(); |
212 | updateStyle(); |
213 | currentUIObject.setVisible(isVisible()); |
214 | } |
215 | |
216 | /** |
217 | * update the Style values |
218 | */ |
219 | public void updateStyle() |
220 | { |
221 | String currentStyle = getStyle(); |
222 | if (Utilities.equals(previousStyle, currentStyle)) |
223 | { |
224 | return; |
225 | } |
226 | |
227 | String oldStyle = previousStyle; |
228 | previousStyle = currentStyle; |
229 | |
230 | HashMap<String, String> oldStyleMap = breakStyleString(oldStyle); |
231 | HashMap<String, String> newStyleMap = breakStyleString(currentStyle); |
232 | |
233 | isolateChanges(oldStyleMap, newStyleMap); |
234 | |
235 | Set<String> removeKeys = oldStyleMap.keySet(); |
236 | for (String keyToRemove : removeKeys) |
237 | { |
238 | removeStyleAttribute(keyToRemove); |
239 | } |
240 | |
241 | Set<Entry<String, String>> entrySet = newStyleMap.entrySet(); |
242 | for (Entry<String, String> entry : entrySet) |
243 | { |
244 | setStyleAttribute(entry.getKey(), entry.getValue()); |
245 | } |
246 | } |
247 | |
248 | /** |
249 | * isolate Changes between a new style map and the old style map. The old |
250 | * style map will only contain the styles to remove and the new one will |
251 | * only contain the styles to change. |
252 | * |
253 | * @param oldStyleMap the old style map |
254 | * @param newStyleMap the new style map |
255 | */ |
256 | public static void isolateChanges( |
257 | HashMap<String, String> oldStyleMap, HashMap<String, String> newStyleMap) |
258 | { |
259 | Object[] oldEntries = oldStyleMap.keySet().toArray(); |
260 | for (Object currentKey : oldEntries) |
261 | { |
262 | if (newStyleMap.containsKey(currentKey)) |
263 | { |
264 | String oldValue = oldStyleMap.get(currentKey); |
265 | String newValue = newStyleMap.get(currentKey); |
266 | if (Utilities.equals(newValue, oldValue)) |
267 | { |
268 | newStyleMap.remove(currentKey); |
269 | } |
270 | oldStyleMap.remove(currentKey); |
271 | } |
272 | } |
273 | } |
274 | |
275 | /** |
276 | * set Style Attribute on the element |
277 | * |
278 | * @param key attribute key |
279 | * @param value attribute value |
280 | */ |
281 | @SuppressWarnings("unused") |
282 | public void setStyleAttribute( |
283 | String key, String value) |
284 | { |
285 | String assignValue = value; |
286 | if (assignValue == null || assignValue.length() == 0) |
287 | { |
288 | assignValue = ""; |
289 | } |
290 | |
291 | try |
292 | { |
293 | UIObject currentUIObject = getUIObject(); |
294 | Element element = currentUIObject.getElement(); |
295 | Style jsStyle = element.getStyle(); |
296 | Utilities.setStyleAttribute(jsStyle, key, assignValue); |
297 | } |
298 | catch (Exception ex) |
299 | { |
300 | getModelRegistry().showErrorException( |
301 | "setStyleAttribute error. Key = \"" + key + "\" Value = \"" |
302 | + value + "\"", ex); |
303 | } |
304 | } |
305 | |
306 | /** |
307 | * remove a Style Attribute from the element |
308 | * |
309 | * @param keyToRemove the style attribute name to remove |
310 | */ |
311 | @SuppressWarnings("unused") |
312 | public void removeStyleAttribute( |
313 | String keyToRemove) |
314 | { |
315 | UIObject currentUIObject = getUIObject(); |
316 | Element element = currentUIObject.getElement(); |
317 | Style jsStyle = element.getStyle(); |
318 | |
319 | Utilities.setStyleAttribute(jsStyle, keyToRemove, ""); |
320 | } |
321 | |
322 | /** |
323 | * break Style String into a map of key and values |
324 | * |
325 | * @param style the style string to break |
326 | * @return the map of attributes and values |
327 | */ |
328 | public static HashMap<String, String> breakStyleString( |
329 | String style) |
330 | { |
331 | HashMap<String, String> retVal = new HashMap<String, String>(); |
332 | |
333 | String simpleStyle = style; |
334 | if (simpleStyle == null) |
335 | { |
336 | simpleStyle = ""; |
337 | } |
338 | else |
339 | { |
340 | simpleStyle = simpleStyle.replaceAll("[\n\r\t]", ""); |
341 | } |
342 | |
343 | String[] split = simpleStyle.split(";"); |
344 | for (String currentValue : split) |
345 | { |
346 | String[] attributeAndValue = currentValue.split(":"); |
347 | String value = null; |
348 | |
349 | if (attributeAndValue.length > 0) |
350 | { |
351 | String attribute = attributeAndValue[0]; |
352 | attribute = attribute.trim(); |
353 | if (attribute.length() > 0) |
354 | { |
355 | if (attributeAndValue.length > 1) |
356 | { |
357 | value = attributeAndValue[1]; |
358 | value = value.trim(); |
359 | } |
360 | retVal.put(attribute, value); |
361 | } |
362 | } |
363 | } |
364 | |
365 | return retVal; |
366 | } |
367 | |
368 | /** |
369 | * get Vertical Alignment based on the name |
370 | * |
371 | * @param verticalAlignment the vertical alignment name |
372 | * @return the matching VerticalAlignmentConstant |
373 | */ |
374 | public static VerticalAlignmentConstant getVerticalAlignment( |
375 | String verticalAlignment) |
376 | { |
377 | if (Utilities.equals(verticalAlignment, "bottom")) |
378 | { |
379 | return HasVerticalAlignment.ALIGN_BOTTOM; |
380 | } |
381 | if (Utilities.equals(verticalAlignment, "middle")) |
382 | { |
383 | return HasVerticalAlignment.ALIGN_MIDDLE; |
384 | } |
385 | return HasVerticalAlignment.ALIGN_TOP; |
386 | } |
387 | |
388 | /** |
389 | * get Horizontal Alignment based on the name |
390 | * |
391 | * @param horizontalAlignment the horizontal alignment name |
392 | * @return the matching HorizontalAlignmentConstant |
393 | */ |
394 | public static HorizontalAlignmentConstant getHorizontalAlignment( |
395 | String horizontalAlignment) |
396 | { |
397 | if (Utilities.equals(horizontalAlignment, "center")) |
398 | { |
399 | return HasHorizontalAlignment.ALIGN_CENTER; |
400 | } |
401 | if (Utilities.equals(horizontalAlignment, "left")) |
402 | { |
403 | return HasHorizontalAlignment.ALIGN_LEFT; |
404 | } |
405 | if (Utilities.equals(horizontalAlignment, "right")) |
406 | { |
407 | return HasHorizontalAlignment.ALIGN_RIGHT; |
408 | } |
409 | return HasHorizontalAlignment.ALIGN_DEFAULT; |
410 | } |
411 | |
412 | /** |
413 | * get Text Alignment based on the alignment name |
414 | * |
415 | * @param usedAlignment the alignment name |
416 | * @return the text alignment constant |
417 | */ |
418 | public static TextAlignment getTextAlignment( |
419 | String usedAlignment) |
420 | { |
421 | if (Utilities.equals(usedAlignment, "right")) |
422 | { |
423 | return TextAlignment.RIGHT; |
424 | } |
425 | else if (Utilities.equals(usedAlignment, "left")) |
426 | { |
427 | return TextAlignment.LEFT; |
428 | } |
429 | else if (Utilities.equals(usedAlignment, "center")) |
430 | { |
431 | return TextAlignment.CENTER; |
432 | } |
433 | else if (Utilities.equals(usedAlignment, "justify")) |
434 | { |
435 | return TextAlignment.JUSTIFY; |
436 | } |
437 | return null; |
438 | } |
439 | |
440 | /** |
441 | * get the Direction based on the Direction name |
442 | * |
443 | * @param usedDirection the Direction name |
444 | * @return the text Direction constant |
445 | */ |
446 | public static Direction getDirection( |
447 | String usedDirection) |
448 | { |
449 | if (Utilities.equals(usedDirection, "default")) |
450 | { |
451 | return Direction.DEFAULT; |
452 | } |
453 | else if (Utilities.equals(usedDirection, "left to right")) |
454 | { |
455 | return Direction.LTR; |
456 | } |
457 | else if (Utilities.equals(usedDirection, "right to left")) |
458 | { |
459 | return Direction.RTL; |
460 | } |
461 | return null; |
462 | } |
463 | |
464 | /** |
465 | * @param setIsVisible the isVisible to set |
466 | */ |
467 | public void setIsVisible( |
468 | boolean setIsVisible) |
469 | { |
470 | isVisible = setIsVisible; |
471 | } |
472 | |
473 | /** |
474 | * @return the isVisible |
475 | */ |
476 | public boolean isVisible() |
477 | { |
478 | return isVisible; |
479 | } |
480 | |
481 | /** |
482 | * @param setName the name to set |
483 | */ |
484 | public void setName( |
485 | String setName) |
486 | { |
487 | name = setName; |
488 | } |
489 | |
490 | /** |
491 | * @return the name |
492 | */ |
493 | public String getName() |
494 | { |
495 | return name; |
496 | } |
497 | |
498 | /** |
499 | * @param setStyle the style to set |
500 | */ |
501 | public void setStyle( |
502 | String setStyle) |
503 | { |
504 | style = setStyle; |
505 | } |
506 | |
507 | /** |
508 | * @return the style |
509 | */ |
510 | public String getStyle() |
511 | { |
512 | return style; |
513 | } |
514 | |
515 | /** |
516 | * @param setUIObject the uiObject to set |
517 | */ |
518 | public void setUIObject( |
519 | UIObject setUIObject) |
520 | { |
521 | uiObject = setUIObject; |
522 | } |
523 | |
524 | /** |
525 | * @param setGwtObjectCreationEnabled set to true to enable the GWT object |
526 | * creation. False otherwise. |
527 | */ |
528 | public static void setGwtObjectCreationEnabled( |
529 | boolean setGwtObjectCreationEnabled) |
530 | { |
531 | GWT_OBJECT_CREATION_ENABLED = setGwtObjectCreationEnabled; |
532 | } |
533 | |
534 | /** |
535 | * @return the gWT_OBJECT_CREATION_ENABLED |
536 | */ |
537 | public static boolean isGwtObjectCreationEnabled() |
538 | { |
539 | return GWT_OBJECT_CREATION_ENABLED; |
540 | } |
541 | |
542 | /** |
543 | * unregister Handler |
544 | * |
545 | * @param handler the handler registration to remove |
546 | */ |
547 | public static void unregisterHandler( |
548 | HandlerRegistration handler) |
549 | { |
550 | if (handler != null) |
551 | { |
552 | handler.removeHandler(); |
553 | } |
554 | } |
555 | |
556 | } |