1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 from warnings import warn
17
18 from muntjac.ui.abstract_field import AbstractField
19 from muntjac.terminal.gwt.client.ui.v_text_field import VTextField
20
21 from muntjac.event.field_events import \
22 BlurEvent, IBlurListener, IBlurNotifier, FocusEvent, IFocusListener, \
23 IFocusNotifier, TextChangeEvent, ITextChangeListener, ITextChangeNotifier,\
24 EVENT_METHOD
25
26
27 -class AbstractTextField(AbstractField, IBlurNotifier, IFocusNotifier,
28 ITextChangeNotifier):
29
31 super(AbstractTextField, self).__init__()
32
33
34 self._format = None
35
36
37 self._nullRepresentation = 'null'
38
39
40
41 self._nullSettingAllowed = False
42
43
44 self._maxLength = -1
45
46
47 self._columns = 0
48
49
50 self._inputPrompt = None
51
52
53
54 self._lastKnownTextContent = None
55
56
57
58 self._lastKnownCursorPosition = None
59
60
61
62
63 self._textChangeEventPending = None
64
65 self._textChangeEventMode = TextChangeEventMode.LAZY
66
67 self._DEFAULT_TEXTCHANGE_TIMEOUT = 400
68
69 self._textChangeEventTimeout = self._DEFAULT_TEXTCHANGE_TIMEOUT
70
71
72 self._selectionPosition = -1
73
74
75 self._selectionLength = None
76
77
78
79
80 self._changingVariables = None
81
82
83 - def paintContent(self, target):
84 super(AbstractTextField, self).paintContent(target)
85
86 if self.getMaxLength() >= 0:
87 target.addAttribute('maxLength', self.getMaxLength())
88
89
90 columns = self.getColumns()
91 if columns != 0:
92 target.addAttribute('cols', str(columns))
93
94 if self.getInputPrompt() is not None:
95 target.addAttribute('prompt', self.getInputPrompt())
96
97
98 value = self.getFormattedValue()
99 if value is None:
100 value = self.getNullRepresentation()
101 if value is None:
102 raise ValueError('Null values are not allowed if '
103 'the null-representation is null')
104
105 target.addVariable(self, 'text', value)
106
107 if self._selectionPosition != -1:
108 target.addAttribute('selpos', self._selectionPosition)
109 target.addAttribute('sellen', self._selectionLength)
110 self._selectionPosition = -1
111
112 if self.hasListeners(TextChangeEvent):
113 target.addAttribute(VTextField.ATTR_TEXTCHANGE_EVENTMODE,
114 str(self.getTextChangeEventMode()))
115 target.addAttribute(VTextField.ATTR_TEXTCHANGE_TIMEOUT,
116 self.getTextChangeTimeout())
117
118 if self._lastKnownTextContent is not None:
119
120
121
122
123
124 target.addAttribute(
125 VTextField.ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS, True)
126
127
129 """Gets the formatted string value. Sets the field value by using
130 the assigned format.
131
132 @return: the Formatted value.
133 @see: L{setFormat}
134 @deprecated:
135 """
136 warn('deprecated', DeprecationWarning)
137
138 v = self.getValue()
139 if v is None:
140 return None
141 return str(v)
142
143
144 - def getValue(self):
145 v = super(AbstractTextField, self).getValue()
146 if self._format is None or v is None:
147 return v
148
149 try:
150 warn('deprecated', DeprecationWarning)
151 return self._format.format(v)
152 except ValueError:
153 return v
154
155
156 - def changeVariables(self, source, variables):
157 self._changingVariables = True
158
159 try:
160 super(AbstractTextField, self).changeVariables(source, variables)
161
162 if VTextField.VAR_CURSOR in variables:
163 obj = variables.get( VTextField.VAR_CURSOR )
164 self._lastKnownCursorPosition = int(obj)
165
166 if VTextField.VAR_CUR_TEXT in variables:
167
168
169
170
171 self.handleInputEventTextChange(variables)
172
173
174 if 'text' in variables and not self.isReadOnly():
175
176
177
178 newValue = variables.get('text')
179
180
181 if (self.getMaxLength() != -1
182 and len(newValue) > self.getMaxLength()):
183 newValue = newValue[:self.getMaxLength()]
184
185 oldValue = self.getFormattedValue()
186
187 if (newValue is not None
188 and (oldValue is None or self.isNullSettingAllowed())
189 and newValue == self.getNullRepresentation()):
190 newValue = None
191
192 if (newValue != oldValue
193 and (newValue is None or newValue != oldValue)):
194 wasModified = self.isModified()
195 self.setValue(newValue, True)
196
197
198
199 if (self._format is not None
200 or wasModified != self.isModified()):
201 self.requestRepaint()
202
203 self.firePendingTextChangeEvent()
204
205 if FocusEvent.EVENT_ID in variables:
206 self.fireEvent( FocusEvent(self) )
207
208 if BlurEvent.EVENT_ID in variables:
209 self.fireEvent( BlurEvent(self) )
210 finally:
211 self._changingVariables = False
212
213
216
217
219 """Gets the null-string representation.
220
221 The null-valued strings are represented on the user interface by
222 replacing the null value with this string. If the null representation
223 is set null (not 'null' string), painting null value throws exception.
224
225 The default value is string 'null'.
226
227 @return: the textual string representation for null strings.
228 @see: L{TextField.isNullSettingAllowed}
229 """
230 return self._nullRepresentation
231
232
234 """Is setting nulls with null-string representation allowed.
235
236 If this property is true, writing null-representation string to text
237 field always sets the field value to real null. If this property is
238 false, null setting is not made, but the null values are maintained.
239 Maintenance of null-values is made by only converting the textfield
240 contents to real null, if the text field matches the null-string
241 representation and the current value of the field is null.
242
243 By default this setting is false
244
245 @return: Should the null-string represenation be always
246 converted to null-values.
247 @see: L{TextField.getNullRepresentation}
248 """
249 return self._nullSettingAllowed
250
251
252 - def setNullRepresentation(self, nullRepresentation):
253 """Sets the null-string representation.
254
255 The null-valued strings are represented on the user interface by
256 replacing the null value with this string. If the null representation
257 is set null (not 'null' string), painting null value throws exception.
258
259 The default value is string 'null'
260
261 @param nullRepresentation:
262 Textual representation for null strings.
263 @see: L{TextField.setNullSettingAllowed}
264 """
265 self._nullRepresentation = nullRepresentation
266 self.requestRepaint()
267
268
269 - def setNullSettingAllowed(self, nullSettingAllowed):
270 """Sets the null conversion mode.
271
272 If this property is true, writing null-representation string to text
273 field always sets the field value to real null. If this property is
274 false, null setting is not made, but the null values are maintained.
275 Maintenance of null-values is made by only converting the textfield
276 contents to real null, if the text field matches the null-string
277 representation and the current value of the field is null.
278
279 By default this setting is false.
280
281 @param nullSettingAllowed:
282 Should the null-string representation always be converted
283 to null-values.
284 @see: L{TextField.getNullRepresentation}
285 """
286 self._nullSettingAllowed = nullSettingAllowed
287 self.requestRepaint()
288
289
291 """Gets the value formatter of TextField.
292
293 @return: the format used to format the value.
294 @deprecated: replaced by L{PropertyFormatter}
295 """
296 warn('replaced by PropertyFormatter', DeprecationWarning)
297 return self._format
298
299
301 """Gets the value formatter of TextField.
302
303 @param fmt:
304 the Format used to format the value. Null disables the
305 formatting.
306 @deprecated: replaced by L{PropertyFormatter}
307 """
308 warn('replaced by PropertyFormatter', DeprecationWarning)
309 self._format = fmt
310 self.requestRepaint()
311
312
314 return (super(AbstractTextField, self).isEmpty()
315 or len(str(self)) == 0)
316
317
318 - def getMaxLength(self):
319 """Returns the maximum number of characters in the field. Value -1 is
320 considered unlimited. Terminal may however have some technical limits.
321
322 @return: the maxLength
323 """
324 return self._maxLength
325
326
327 - def setMaxLength(self, maxLength):
328 """Sets the maximum number of characters in the field. Value -1 is
329 considered unlimited. Terminal may however have some technical limits.
330
331 @param maxLength:
332 the maxLength to set
333 """
334 self._maxLength = maxLength
335 self.requestRepaint()
336
337
338 - def getColumns(self):
339 """Gets the number of columns in the editor. If the number of columns
340 is set 0, the actual number of displayed columns is determined
341 implicitly by the adapter.
342
343 @return: the number of columns in the editor.
344 """
345 return self._columns
346
347
348 - def setColumns(self, columns):
349 """Sets the number of columns in the editor. If the number of columns
350 is set 0, the actual number of displayed columns is determined
351 implicitly by the adapter.
352
353 @param columns:
354 the number of columns to set.
355 """
356 if columns < 0:
357 columns = 0
358 self._columns = columns
359 self.requestRepaint()
360
361
363 """Gets the current input prompt.
364
365 @see: L{setInputPrompt}
366 @return: the current input prompt, or null if not enabled
367 """
368 return self._inputPrompt
369
370
372 """Sets the input prompt - a textual prompt that is displayed when
373 the field would otherwise be empty, to prompt the user for input.
374 """
375 self._inputPrompt = inputPrompt
376 self.requestRepaint()
377
378
380 if self._textChangeEventPending:
381 self._textChangeEventPending = False
382 self.fireEvent( TextChangeEventImpl(self) )
383
384
385 - def setInternalValue(self, newValue):
386 if self._changingVariables and not self._textChangeEventPending:
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404 if (newValue is None and self._lastKnownTextContent is not None
405 and self._lastKnownTextContent != \
406 self.getNullRepresentation()):
407
408 self._lastKnownTextContent = self.getNullRepresentation()
409 self._textChangeEventPending = True
410 elif (newValue is not None
411 and str(newValue) != self._lastKnownTextContent):
412
413
414 self._lastKnownTextContent = str(newValue)
415 self._textChangeEventPending = True
416
417 self.firePendingTextChangeEvent()
418
419
420
421 self._lastKnownTextContent = None
422 super(AbstractTextField, self).setInternalValue(newValue)
423
424
425 - def setValue(self, newValue, repaintIsNotNeeded=None):
426 if repaintIsNotNeeded is not None:
427 super(AbstractTextField, self).setValue(newValue,
428 repaintIsNotNeeded)
429 else:
430 super(AbstractTextField, self).setValue(newValue)
431
432
433
434
435
436
437
438 if self._lastKnownTextContent is not None:
439 self._lastKnownTextContent = None
440 self.requestRepaint()
441
442
444
445
446
447
448 obj = variables.get(VTextField.VAR_CUR_TEXT)
449 self._lastKnownTextContent = obj
450 self._textChangeEventPending = True
451
452
453 - def setTextChangeEventMode(self, inputEventMode):
454 """Sets the mode how the TextField triggers L{TextChangeEvent}s.
455
456 @param inputEventMode: the new mode
457
458 @see: L{TextChangeEventMode}
459 """
460 self._textChangeEventMode = inputEventMode
461 self.requestRepaint()
462
463
465 """@return: the mode used to trigger L{TextChangeEvent}s."""
466 return self._textChangeEventMode
467
468
469 - def addListener(self, listener, iface=None):
470 if (isinstance(listener, IBlurListener) and
471 (iface is None or issubclass(iface, IBlurListener))):
472 self.registerListener(BlurEvent.EVENT_ID, BlurEvent,
473 listener, IBlurListener.blurMethod)
474
475 if (isinstance(listener, IFocusListener) and
476 (iface is None or issubclass(iface, IFocusListener))):
477 self.registerListener(FocusEvent.EVENT_ID, FocusEvent,
478 listener, IFocusListener.focusMethod)
479
480 if (isinstance(listener, ITextChangeListener) and
481 (iface is None or issubclass(iface, ITextChangeListener))):
482 self.registerListener(ITextChangeListener.EVENT_ID,
483 TextChangeEvent, listener, EVENT_METHOD)
484
485 super(AbstractTextField, self).addListener(listener, iface)
486
487
488 - def addCallback(self, callback, eventType=None, *args):
489 if eventType is None:
490 eventType = callback._eventType
491
492 if issubclass(eventType, BlurEvent):
493 self.registerCallback(BlurEvent, callback,
494 BlurEvent.EVENT_ID, *args)
495
496 elif issubclass(eventType, FocusEvent):
497 self.registerCallback(FocusEvent, callback,
498 FocusEvent.EVENT_ID, *args)
499
500 elif issubclass(eventType, TextChangeEvent):
501 self.registerCallback(TextChangeEvent, callback,
502 ITextChangeListener.EVENT_ID, *args)
503
504 else:
505 super(AbstractTextField, self).addCallback(callback,
506 eventType, *args)
507
508
509 - def removeListener(self, listener, iface=None):
510 if (isinstance(listener, IBlurListener) and
511 (iface is None or issubclass(iface, IBlurListener))):
512 self.withdrawListener(BlurEvent.EVENT_ID, BlurEvent, listener)
513
514 if (isinstance(listener, IFocusListener) and
515 (iface is None or issubclass(iface, IFocusListener))):
516 self.withdrawListener(FocusEvent.EVENT_ID, FocusEvent, listener)
517
518 if (isinstance(listener, ITextChangeListener) and
519 (iface is None or issubclass(iface, ITextChangeListener))):
520 self.withdrawListener(ITextChangeListener.EVENT_ID,
521 TextChangeEvent, listener)
522
523 super(AbstractTextField, self).addListener(listener, iface)
524
525
526 - def removeCallback(self, callback, eventType=None):
527 if eventType is None:
528 eventType = callback._eventType
529
530 if issubclass(eventType, BlurEvent):
531 self.withdrawCallback(BlurEvent, callback, BlurEvent.EVENT_ID)
532
533 elif issubclass(eventType, FocusEvent):
534 self.withdrawCallback(FocusEvent, callback, FocusEvent.EVENT_ID)
535
536 elif issubclass(eventType, TextChangeEvent):
537 self.withdrawCallback(TextChangeEvent, callback,
538 ITextChangeListener.EVENT_ID)
539
540 else:
541 super(AbstractTextField, self).removeCallback(callback, eventType)
542
543
544 - def setTextChangeTimeout(self, timeout):
545 """The text change timeout modifies how often text change events
546 are communicated to the application when
547 L{getTextChangeEventMode} is L{TextChangeEventMode.LAZY}
548 or L{TextChangeEventMode.TIMEOUT}.
549
550 @see: L{getTextChangeEventMode}
551
552 @param timeout: the timeout in milliseconds
553 """
554 self._textChangeEventTimeout = timeout
555 self.requestRepaint()
556
557
559 """Gets the timeout used to fire L{TextChangeEvent}s when the
560 L{getTextChangeEventMode} is L{TextChangeEventMode.LAZY}
561 or L{TextChangeEventMode.TIMEOUT}.
562
563 @return: the timeout value in milliseconds
564 """
565 return self._textChangeEventTimeout
566
567
569 """Gets the current (or the last known) text content in the field.
570
571 Note the text returned by this method is not necessary the same that
572 is returned by the L{getValue} method. The value is updated
573 when the terminal fires a value change event via e.g. blurring the
574 field or by pressing enter. The value returned by this method is
575 updated also on L{TextChangeEvent}s. Due to this high dependency
576 to the terminal implementation this method is (at least at this
577 point) not published.
578
579 @return: the text which is currently displayed in the field.
580 """
581 if self._lastKnownTextContent is not None:
582 return self._lastKnownTextContent
583 else:
584 text = self.getValue()
585 if text is None:
586 return self.getNullRepresentation()
587 return str(text)
588
589
590 - def selectAll(self):
591 """Selects all text in the field.
592 """
593 text = '' if self.getValue() is None else str(self.getValue())
594 self.setSelectionRange(0, len(text))
595
596
597 - def setSelectionRange(self, pos, length):
598 """Sets the range of text to be selected.
599
600 As a side effect the field will become focused.
601
602 @param pos:
603 the position of the first character to be selected
604 @param length:
605 the number of characters to be selected
606 """
607 self._selectionPosition = pos
608 self._selectionLength = length
609 self.focus()
610 self.requestRepaint()
611
612
613 - def setCursorPosition(self, pos):
614 """Sets the cursor position in the field. As a side effect the
615 field will become focused.
616
617 @param pos:
618 the position for the cursor
619 """
620 self.setSelectionRange(pos, 0)
621 self._lastKnownCursorPosition = pos
622
623
625 """Returns the last known cursor position of the field.
626
627 Note that due to the client server nature or the GWT terminal, Muntjac
628 cannot provide the exact value of the cursor position in most
629 situations. The value is updated only when the client side terminal
630 communicates to TextField, like on L{ValueChangeEvent}s and
631 L{TextChangeEvent}s. This may change later if a deep push
632 integration is built to Muntjac.
633
634 @return: the cursor position
635 """
636 return self._lastKnownCursorPosition
637
638
639 -class TextChangeEventMode(object):
640 """Different modes how the TextField can trigger L{TextChangeEvent}s.
641 """
642
643
644
645 EAGER = 'EAGER'
646
647
648
649
650
651
652
653
654
655
656
657 TIMEOUT = 'TIMEOUT'
658
659
660
661
662
663
664
665
666
667 LAZY = 'LAZY'
668
669 _values = [EAGER, TIMEOUT, LAZY]
670
671 @classmethod
673 return cls._values[:]
674
675
676 -class TextChangeEventImpl(TextChangeEvent):
677
678 - def __init__(self, tf):
679 super(TextChangeEventImpl, self).__init__(tf)
680 self._curText = tf.getCurrentTextContent()
681 self._cursorPosition = tf.getCursorPosition()
682
683
684 - def getComponent(self):
686
687
690
691
693 return self._cursorPosition
694