1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.vaadin.client.ui;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Date;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Set;
26
27 import com.google.gwt.aria.client.Roles;
28 import com.google.gwt.core.client.Scheduler;
29 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
30 import com.google.gwt.dom.client.Style;
31 import com.google.gwt.dom.client.Style.Display;
32 import com.google.gwt.dom.client.Style.Unit;
33 import com.google.gwt.event.dom.client.BlurEvent;
34 import com.google.gwt.event.dom.client.BlurHandler;
35 import com.google.gwt.event.dom.client.ClickEvent;
36 import com.google.gwt.event.dom.client.ClickHandler;
37 import com.google.gwt.event.dom.client.FocusEvent;
38 import com.google.gwt.event.dom.client.FocusHandler;
39 import com.google.gwt.event.dom.client.KeyCodes;
40 import com.google.gwt.event.dom.client.KeyDownEvent;
41 import com.google.gwt.event.dom.client.KeyDownHandler;
42 import com.google.gwt.event.dom.client.KeyUpEvent;
43 import com.google.gwt.event.dom.client.KeyUpHandler;
44 import com.google.gwt.event.dom.client.LoadEvent;
45 import com.google.gwt.event.dom.client.LoadHandler;
46 import com.google.gwt.event.logical.shared.CloseEvent;
47 import com.google.gwt.event.logical.shared.CloseHandler;
48 import com.google.gwt.user.client.Command;
49 import com.google.gwt.user.client.DOM;
50 import com.google.gwt.user.client.Element;
51 import com.google.gwt.user.client.Event;
52 import com.google.gwt.user.client.Timer;
53 import com.google.gwt.user.client.Window;
54 import com.google.gwt.user.client.ui.Composite;
55 import com.google.gwt.user.client.ui.FlowPanel;
56 import com.google.gwt.user.client.ui.HTML;
57 import com.google.gwt.user.client.ui.Image;
58 import com.google.gwt.user.client.ui.PopupPanel;
59 import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
60 import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
61 import com.google.gwt.user.client.ui.TextBox;
62 import com.vaadin.client.ApplicationConnection;
63 import com.vaadin.client.BrowserInfo;
64 import com.vaadin.client.ComponentConnector;
65 import com.vaadin.client.ComputedStyle;
66 import com.vaadin.client.ConnectorMap;
67 import com.vaadin.client.Focusable;
68 import com.vaadin.client.UIDL;
69 import com.vaadin.client.Util;
70 import com.vaadin.client.VConsole;
71 import com.vaadin.client.ui.aria.AriaHelper;
72 import com.vaadin.client.ui.aria.HandlesAriaCaption;
73 import com.vaadin.client.ui.aria.HandlesAriaInvalid;
74 import com.vaadin.client.ui.aria.HandlesAriaRequired;
75 import com.vaadin.client.ui.menubar.MenuBar;
76 import com.vaadin.client.ui.menubar.MenuItem;
77 import com.vaadin.shared.AbstractComponentState;
78 import com.vaadin.shared.EventId;
79 import com.vaadin.shared.ui.ComponentStateUtil;
80 import com.vaadin.shared.ui.combobox.FilteringMode;
81
82
83
84
85
86
87 @SuppressWarnings("deprecation")
88 public class VFilterSelectPatched extends Composite implements Field, KeyDownHandler,
89 KeyUpHandler, ClickHandler, FocusHandler, BlurHandler, Focusable,
90 SubPartAware, HandlesAriaCaption, HandlesAriaInvalid,
91 HandlesAriaRequired {
92
93
94
95
96 public class FilterSelectSuggestion implements Suggestion, Command {
97
98 private final String key;
99 private final String caption;
100 private String iconUri;
101
102
103
104
105
106
107
108 public FilterSelectSuggestion(UIDL uidl) {
109 key = uidl.getStringAttribute("key");
110 caption = uidl.getStringAttribute("caption");
111 if (uidl.hasAttribute("icon")) {
112 iconUri = client.translateVaadinUri(uidl
113 .getStringAttribute("icon"));
114 }
115 }
116
117
118
119
120
121
122
123 @Override
124 public String getDisplayString() {
125 final StringBuffer sb = new StringBuffer();
126 if (iconUri != null) {
127 sb.append("<img src=\"");
128 sb.append(Util.escapeAttribute(iconUri));
129 sb.append("\" alt=\"\" class=\"v-icon\" />");
130 }
131 String content;
132 if ("".equals(caption)) {
133
134
135 content = " ";
136 } else {
137 content = Util.escapeHTML(caption);
138 }
139 sb.append("<span>" + content + "</span>");
140 return sb.toString();
141 }
142
143
144
145
146
147 @Override
148 public String getReplacementString() {
149 return caption;
150 }
151
152
153
154
155
156
157 public String getOptionKey() {
158 return key;
159 }
160
161
162
163
164
165
166 public String getIconUri() {
167 return iconUri;
168 }
169
170
171
172
173
174 @Override
175 public void execute() {
176 onSuggestionSelected(this);
177 }
178
179 @Override
180 public boolean equals(Object obj) {
181 if (!(obj instanceof FilterSelectSuggestion)) {
182 return false;
183 }
184 FilterSelectSuggestion other = (FilterSelectSuggestion) obj;
185 if ((key == null && other.key != null)
186 || (key != null && !key.equals(other.key))) {
187 return false;
188 }
189 if ((caption == null && other.caption != null)
190 || (caption != null && !caption.equals(other.caption))) {
191 return false;
192 }
193 if ((iconUri == null && other.iconUri != null)
194 || (iconUri != null && !iconUri.equals(other.iconUri))) {
195 return false;
196 }
197 return true;
198 }
199 }
200
201
202
203
204
205 public class SuggestionPopup extends VOverlay implements PositionCallback,
206 CloseHandler<PopupPanel> {
207
208 private static final int Z_INDEX = 30000;
209
210
211 public final SuggestionMenu menu;
212
213 private final Element up = DOM.createDiv();
214 private final Element down = DOM.createDiv();
215 private final Element status = DOM.createDiv();
216
217 private boolean isPagingEnabled = true;
218
219 private long lastAutoClosed;
220
221 private int popupOuterPadding = -1;
222
223 private int topPosition;
224
225
226
227
228 SuggestionPopup() {
229 super(true, false, true);
230 debug("VFS.SP: constructor()");
231 setOwner(VFilterSelectPatched.this);
232 menu = new SuggestionMenu();
233 setWidget(menu);
234
235 getElement().getStyle().setZIndex(Z_INDEX);
236
237 final Element root = getContainerElement();
238
239 up.setInnerHTML("<span>Prev</span>");
240 DOM.sinkEvents(up, Event.ONCLICK);
241
242 down.setInnerHTML("<span>Next</span>");
243 DOM.sinkEvents(down, Event.ONCLICK);
244
245 root.insertFirst(up);
246 root.appendChild(down);
247 root.appendChild(status);
248
249 DOM.sinkEvents(root, Event.ONMOUSEDOWN | Event.ONMOUSEWHEEL);
250 addCloseHandler(this);
251
252 Roles.getListRole().set(getElement());
253 }
254
255
256
257
258
259
260
261
262
263
264
265 public void showSuggestions(
266 final Collection<FilterSelectSuggestion> currentSuggestions,
267 final int currentPage, final int totalSuggestions) {
268
269 if (enableDebug) {
270 debug("VFS.SP: showSuggestions(" + currentSuggestions + ", "
271 + currentPage + ", " + totalSuggestions + ")");
272 }
273
274
275
276
277
278
279
280
281 final SuggestionPopup popup = this;
282 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
283 @Override
284 public void execute() {
285
286 getElement().setId("VAADIN_COMBOBOX_OPTIONLIST");
287
288 menu.setSuggestions(currentSuggestions);
289 final int x = VFilterSelectPatched.this.getAbsoluteLeft();
290
291 topPosition = tb.getAbsoluteTop();
292 topPosition += tb.getOffsetHeight();
293
294 setPopupPosition(x, topPosition);
295
296 int nullOffset = (nullSelectionAllowed
297 && "".equals(lastFilter) ? 1 : 0);
298 boolean firstPage = (currentPage == 0);
299 final int first = currentPage * pageLength + 1
300 - (firstPage ? 0 : nullOffset);
301 final int last = first
302 + currentSuggestions.size()
303 - 1
304 - (firstPage && "".equals(lastFilter) ? nullOffset
305 : 0);
306 final int matches = totalSuggestions - nullOffset;
307 if (last > 0) {
308
309 status.setInnerText((matches == 0 ? 0 : first) + "-"
310 + last + "/" + matches);
311 } else {
312 status.setInnerText("");
313 }
314
315
316
317 if (totalSuggestions <= pageLength || pageLength == 0) {
318 setPagingEnabled(false);
319 } else {
320 setPagingEnabled(true);
321 }
322 setPrevButtonActive(first > 1);
323 setNextButtonActive(last < matches);
324
325
326 menu.setWidth("");
327 menu.getElement().getFirstChildElement().getStyle()
328 .clearWidth();
329
330 setPopupPositionAndShow(popup);
331 }
332 });
333 }
334
335
336
337
338
339
340 private void setNextButtonActive(boolean active) {
341 if (enableDebug) {
342 debug("VFS.SP: setNextButtonActive(" + active + ")");
343 }
344 if (active) {
345 DOM.sinkEvents(down, Event.ONCLICK);
346 down.setClassName(VFilterSelectPatched.this.getStylePrimaryName()
347 + "-nextpage");
348 } else {
349 DOM.sinkEvents(down, 0);
350 down.setClassName(VFilterSelectPatched.this.getStylePrimaryName()
351 + "-nextpage-off");
352 }
353 }
354
355
356
357
358
359
360 private void setPrevButtonActive(boolean active) {
361 if (enableDebug) {
362 debug("VFS.SP: setPrevButtonActive(" + active + ")");
363 }
364
365 if (active) {
366 DOM.sinkEvents(up, Event.ONCLICK);
367 up.setClassName(VFilterSelectPatched.this.getStylePrimaryName()
368 + "-prevpage");
369 } else {
370 DOM.sinkEvents(up, 0);
371 up.setClassName(VFilterSelectPatched.this.getStylePrimaryName()
372 + "-prevpage-off");
373 }
374
375 }
376
377
378
379
380 public void selectNextItem() {
381 debug("VFS.SP: selectNextItem()");
382 final MenuItem cur = menu.getSelectedItem();
383 final int index = 1 + menu.getItems().indexOf(cur);
384 if (menu.getItems().size() > index) {
385 final MenuItem newSelectedItem = menu.getItems().get(index);
386 menu.selectItem(newSelectedItem);
387 tb.setText(newSelectedItem.getText());
388 tb.setSelectionRange(lastFilter.length(), newSelectedItem
389 .getText().length() - lastFilter.length());
390
391 } else if (hasNextPage()) {
392 selectPopupItemWhenResponseIsReceived = Select.FIRST;
393 filterOptions(currentPage + 1, lastFilter);
394 }
395 }
396
397
398
399
400 public void selectPrevItem() {
401 debug("VFS.SP: selectPrevItem()");
402 final MenuItem cur = menu.getSelectedItem();
403 final int index = -1 + menu.getItems().indexOf(cur);
404 if (index > -1) {
405 final MenuItem newSelectedItem = menu.getItems().get(index);
406 menu.selectItem(newSelectedItem);
407 tb.setText(newSelectedItem.getText());
408 tb.setSelectionRange(lastFilter.length(), newSelectedItem
409 .getText().length() - lastFilter.length());
410 } else if (index == -1) {
411 if (currentPage > 0) {
412 selectPopupItemWhenResponseIsReceived = Select.LAST;
413 filterOptions(currentPage - 1, lastFilter);
414 }
415 } else {
416 final MenuItem newSelectedItem = menu.getItems().get(
417 menu.getItems().size() - 1);
418 menu.selectItem(newSelectedItem);
419 tb.setText(newSelectedItem.getText());
420 tb.setSelectionRange(lastFilter.length(), newSelectedItem
421 .getText().length() - lastFilter.length());
422 }
423 }
424
425
426
427
428
429 private LazyPageScroller lazyPageScroller = new LazyPageScroller();
430
431 private class LazyPageScroller extends Timer {
432 private int pagesToScroll = 0;
433
434 @Override
435 public void run() {
436 debug("VFS.SP.LPS: run()");
437 if (pagesToScroll != 0) {
438 if (!waitingForFilteringResponse) {
439
440
441
442
443
444
445
446
447
448
449
450 filterOptions(currentPage + pagesToScroll, lastFilter);
451 }
452 pagesToScroll = 0;
453 }
454 }
455
456 public void scrollUp() {
457 debug("VFS.SP.LPS: scrollUp()");
458 if (pageLength > 0 && currentPage + pagesToScroll > 0) {
459 pagesToScroll--;
460 cancel();
461 schedule(200);
462 }
463 }
464
465 public void scrollDown() {
466 debug("VFS.SP.LPS: scrollDown()");
467 if (pageLength > 0
468 && totalMatches > (currentPage + pagesToScroll + 1)
469 * pageLength) {
470 pagesToScroll++;
471 cancel();
472 schedule(200);
473 }
474 }
475 }
476
477
478
479
480
481
482
483
484
485 @Override
486 public void onBrowserEvent(Event event) {
487 debug("VFS.SP: onBrowserEvent()");
488 if (event.getTypeInt() == Event.ONCLICK) {
489 final Element target = DOM.eventGetTarget(event);
490 if (target == up || target == DOM.getChild(up, 0)) {
491 lazyPageScroller.scrollUp();
492 } else if (target == down || target == DOM.getChild(down, 0)) {
493 lazyPageScroller.scrollDown();
494 }
495 } else if (event.getTypeInt() == Event.ONMOUSEWHEEL) {
496 int velocity = event.getMouseWheelVelocityY();
497 if (velocity > 0) {
498 lazyPageScroller.scrollDown();
499 } else {
500 lazyPageScroller.scrollUp();
501 }
502 }
503
504
505
506
507
508 handleMouseDownEvent(event);
509 }
510
511
512
513
514
515
516
517
518
519
520 public void setPagingEnabled(boolean paging) {
521 debug("VFS.SP: setPagingEnabled(" + paging + ")");
522 if (isPagingEnabled == paging) {
523 return;
524 }
525 if (paging) {
526 down.getStyle().clearDisplay();
527 up.getStyle().clearDisplay();
528 status.getStyle().clearDisplay();
529 } else {
530 down.getStyle().setDisplay(Display.NONE);
531 up.getStyle().setDisplay(Display.NONE);
532 status.getStyle().setDisplay(Display.NONE);
533 }
534 isPagingEnabled = paging;
535 }
536
537
538
539
540
541
542
543
544
545 @Override
546 public void setPosition(int offsetWidth, int offsetHeight) {
547 debug("VFS.SP: setPosition()");
548
549 int top = topPosition;
550 int left = getPopupLeft();
551
552
553 menu.setHeight("");
554 if (currentPage > 0 && !hasNextPage()) {
555
556 menu.fixHeightTo(pageLength);
557 }
558
559 final int desiredHeight = offsetHeight = getOffsetHeight();
560 final int desiredWidth = getMainWidth();
561
562 debug("VFS.SP: desired[" + desiredWidth + ", " + desiredHeight
563 + "]");
564
565 Element menuFirstChild = menu.getElement().getFirstChildElement().cast();
566 final int naturalMenuWidth = menuFirstChild.getOffsetWidth();
567
568 if (popupOuterPadding == -1) {
569 popupOuterPadding = Util
570 .measureHorizontalPaddingAndBorder(getElement(), 2);
571 }
572
573 if (naturalMenuWidth < desiredWidth) {
574 menu.setWidth((desiredWidth - popupOuterPadding) + "px");
575 menuFirstChild.getStyle().setWidth(100, Unit.PCT);
576 }
577
578 if (BrowserInfo.get().isIE()) {
579
580
581
582
583 int rootWidth = Math.max(desiredWidth, naturalMenuWidth)
584 - popupOuterPadding;
585 getContainerElement().getStyle().setWidth(rootWidth, Unit.PX);
586 }
587
588 final int vfsHeight = VFilterSelectPatched.this.getOffsetHeight();
589 final int spaceAvailableAbove = top - vfsHeight;
590 final int spaceAvailableBelow = Window.getClientHeight() - top;
591 if (spaceAvailableBelow < offsetHeight
592 && spaceAvailableBelow < spaceAvailableAbove) {
593
594 top -= offsetHeight + vfsHeight;
595 if (top < 0) {
596 offsetHeight += top;
597 top = 0;
598 }
599 } else {
600 offsetHeight = Math.min(offsetHeight, spaceAvailableBelow);
601 }
602
603
604 offsetWidth = menuFirstChild.getOffsetWidth();
605
606 if (offsetHeight < desiredHeight) {
607 int menuHeight = offsetHeight;
608 if (isPagingEnabled) {
609 menuHeight -= up.getOffsetHeight() + down.getOffsetHeight()
610 + status.getOffsetHeight();
611 } else {
612 final ComputedStyle s = new ComputedStyle(menu.getElement());
613 menuHeight -= s.getIntProperty("marginBottom")
614 + s.getIntProperty("marginTop");
615 }
616
617
618 if (menuHeight <= 0) {
619 menuHeight = 100;
620 }
621
622 menu.setHeight(menuHeight + "px");
623
624 final int naturalMenuWidthPlusScrollBar = naturalMenuWidth
625 + Util.getNativeScrollbarSize();
626 if (offsetWidth < naturalMenuWidthPlusScrollBar) {
627 menu.setWidth(naturalMenuWidthPlusScrollBar + "px");
628 }
629 }
630
631 if (offsetWidth + left > Window.getClientWidth()) {
632 left = VFilterSelectPatched.this.getAbsoluteLeft()
633 + VFilterSelectPatched.this.getOffsetWidth() - offsetWidth;
634 if (left < 0) {
635 left = 0;
636 menu.setWidth(Window.getClientWidth() + "px");
637 }
638 }
639
640 setPopupPosition(left, top);
641 menu.scrollSelectionIntoView();
642 }
643
644
645
646
647
648
649 public boolean isJustClosed() {
650 debug("VFS.SP: justClosed()");
651 final long now = (new Date()).getTime();
652 return (lastAutoClosed > 0 && (now - lastAutoClosed) < 200);
653 }
654
655
656
657
658
659
660
661
662
663 @Override
664 public void onClose(CloseEvent<PopupPanel> event) {
665 if (enableDebug) {
666 debug("VFS.SP: onClose(" + event.isAutoClosed() + ")");
667 }
668 if (event.isAutoClosed()) {
669 lastAutoClosed = (new Date()).getTime();
670 }
671 }
672
673
674
675
676
677
678
679
680
681 public void updateStyleNames(UIDL uidl,
682 AbstractComponentState componentState) {
683 debug("VFS.SP: updateStyleNames()");
684 setStyleName(VFilterSelectPatched.this.getStylePrimaryName()
685 + "-suggestpopup");
686 menu.setStyleName(VFilterSelectPatched.this.getStylePrimaryName()
687 + "-suggestmenu");
688 status.setClassName(VFilterSelectPatched.this.getStylePrimaryName()
689 + "-status");
690 if (ComponentStateUtil.hasStyles(componentState)) {
691 for (String style : componentState.styles) {
692 if (!"".equals(style)) {
693 addStyleDependentName(style);
694 }
695 }
696 }
697 }
698
699 }
700
701
702
703
704 public class SuggestionMenu extends MenuBar implements SubPartAware,
705 LoadHandler {
706
707
708
709
710
711
712 private MenuItem keyboardSelectedItem;
713
714 private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(
715 100, new ScheduledCommand() {
716
717 @Override
718 public void execute() {
719 debug("VFS.SM: delayedImageLoadExecutioner()");
720 if (suggestionPopup.isVisible()
721 && suggestionPopup.isAttached()) {
722 setWidth("");
723 getElement().getFirstChildElement().getStyle()
724 .clearWidth();
725 suggestionPopup
726 .setPopupPositionAndShow(suggestionPopup);
727 }
728
729 }
730 });
731
732
733
734
735 SuggestionMenu() {
736 super(true);
737 debug("VFS.SM: constructor()");
738 addDomHandler(this, LoadEvent.getType());
739 DOM.setStyleAttribute(getElement(), "overflowY", Style.Overflow.AUTO.getCssName());
740 }
741
742
743
744
745
746
747 private void scrollItemIntoView(MenuItem item) {
748 if (item != null) {
749 item.getElement().scrollIntoView();
750 }
751 }
752
753 public void scrollSelectionIntoView() {
754 scrollItemIntoView(getSelectedItem());
755 }
756
757
758
759
760
761 public void fixHeightTo(int pagelenth) {
762 if (currentSuggestions.size() > 0) {
763 final int pixels = pagelenth * (getOffsetHeight() - 2)
764 / currentSuggestions.size();
765 setHeight((pixels + 2) + "px");
766 }
767 }
768
769
770
771
772
773
774
775 public void setSuggestions(
776 Collection<FilterSelectSuggestion> suggestions) {
777 if (enableDebug) {
778 debug("VFS.SM: setSuggestions(" + suggestions + ")");
779 }
780
781
782 setKeyboardSelectedItem(null);
783
784 clearItems();
785 final Iterator<FilterSelectSuggestion> it = suggestions.iterator();
786 while (it.hasNext()) {
787 final FilterSelectSuggestion s = it.next();
788 final MenuItem mi = new MenuItem(s.getDisplayString(), true, s);
789 Roles.getListitemRole().set(mi.getElement());
790
791 Util.sinkOnloadForImages(mi.getElement());
792
793 this.addItem(mi);
794 if (s == currentSuggestion) {
795 selectItem(mi);
796 }
797 }
798 }
799
800
801
802
803
804 public void doSelectedItemAction() {
805 debug("VFS.SM: doSelectedItemAction()");
806
807 final String enteredItemValue = tb.getText();
808 if (nullSelectionAllowed && "".equals(enteredItemValue)
809 && selectedOptionKey != null
810 && !"".equals(selectedOptionKey)) {
811 if (nullSelectItem) {
812 reset();
813 return;
814 }
815
816
817 client.updateVariable(paintableId, "filter", "", false);
818 client.updateVariable(paintableId, "page", 0, false);
819 client.updateVariable(paintableId, "selected", new String[] {},
820 immediate);
821 suggestionPopup.hide();
822 return;
823 }
824
825 updateSelectionWhenReponseIsReceived = waitingForFilteringResponse;
826 if (!waitingForFilteringResponse) {
827 doPostFilterSelectedItemAction();
828 }
829 }
830
831
832
833
834 public void doPostFilterSelectedItemAction() {
835 debug("VFS.SM: doPostFilterSelectedItemAction()");
836 final MenuItem item = getSelectedItem();
837 final String enteredItemValue = tb.getText();
838
839 updateSelectionWhenReponseIsReceived = false;
840
841
842 int p = getItems().size();
843 if (p > 0) {
844 for (int i = 0; i < p; i++) {
845 final MenuItem potentialExactMatch = getItems().get(i);
846 if (potentialExactMatch.getText().equals(enteredItemValue)) {
847 selectItem(potentialExactMatch);
848
849
850 if (!"".equals(enteredItemValue)
851 || (selectedOptionKey != null && !""
852 .equals(selectedOptionKey))) {
853 doItemAction(potentialExactMatch, true);
854 }
855 suggestionPopup.hide();
856 return;
857 }
858 }
859 }
860 if (allowNewItem) {
861
862 if (!prompting && !enteredItemValue.equals(lastNewItemString)) {
863
864
865
866 lastNewItemString = enteredItemValue;
867 client.updateVariable(paintableId, "newitem",
868 enteredItemValue, immediate);
869 }
870 } else if (item != null
871 && !"".equals(lastFilter)
872 && (filteringmode == FilteringMode.CONTAINS ? item
873 .getText().toLowerCase()
874 .contains(lastFilter.toLowerCase()) : item
875 .getText().toLowerCase()
876 .startsWith(lastFilter.toLowerCase()))) {
877 doItemAction(item, true);
878 } else {
879
880 if (currentSuggestion != null
881 && !currentSuggestion.key.equals("")) {
882
883 String text = currentSuggestion.getReplacementString();
884 tb.setText(text);
885 selectedOptionKey = currentSuggestion.key;
886 } else {
887
888 tb.setText("");
889 selectedOptionKey = null;
890 }
891 }
892 suggestionPopup.hide();
893 }
894
895 private static final String SUBPART_PREFIX = "item";
896
897 @Override
898 public Element getSubPartElement(String subPart) {
899 int index = Integer.parseInt(subPart.substring(SUBPART_PREFIX
900 .length()));
901
902 MenuItem item = getItems().get(index);
903
904 return item.getElement();
905 }
906
907 @Override
908 public String getSubPartName(Element subElement) {
909 if (!getElement().isOrHasChild(subElement)) {
910 return null;
911 }
912
913 Element menuItemRoot = subElement;
914 while (menuItemRoot != null
915 && !menuItemRoot.getTagName().equalsIgnoreCase("td")) {
916 menuItemRoot = menuItemRoot.getParentElement().cast();
917 }
918
919
920 final int itemCount = getItems().size();
921 for (int i = 0; i < itemCount; i++) {
922 if (getItems().get(i).getElement() == menuItemRoot) {
923 String name = SUBPART_PREFIX + i;
924 return name;
925 }
926 }
927 return null;
928 }
929
930 @Override
931 public void onLoad(LoadEvent event) {
932 debug("VFS.SM: onLoad()");
933
934
935 delayedImageLoadExecutioner.trigger();
936
937 }
938
939 public void selectFirstItem() {
940 debug("VFS.SM: selectFirstItem()");
941 MenuItem firstItem = getItems().get(0);
942 selectItem(firstItem);
943 }
944
945 private MenuItem getKeyboardSelectedItem() {
946 return keyboardSelectedItem;
947 }
948
949 public void setKeyboardSelectedItem(MenuItem firstItem) {
950 keyboardSelectedItem = firstItem;
951 }
952
953 public void selectLastItem() {
954 debug("VFS.SM: selectLastItem()");
955 List<MenuItem> items = getItems();
956 MenuItem lastItem = items.get(items.size() - 1);
957 selectItem(lastItem);
958 }
959 }
960
961
962
963
964
965
966
967 public class FilterSelectTextBox extends TextBox {
968
969
970
971
972 @Override
973 public void setSelectionRange(int pos, int length) {
974 if (textInputEnabled) {
975 super.setSelectionRange(pos, length);
976 } else {
977 super.setSelectionRange(getValue().length(), 0);
978 }
979 }
980 }
981
982 @Deprecated
983 public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF;
984 @Deprecated
985 public static final FilteringMode FILTERINGMODE_STARTSWITH = FilteringMode.STARTSWITH;
986 @Deprecated
987 public static final FilteringMode FILTERINGMODE_CONTAINS = FilteringMode.CONTAINS;
988
989 public static final String CLASSNAME = "v-filterselect";
990 private static final String STYLE_NO_INPUT = "no-input";
991
992
993 public int pageLength = 10;
994
995 private boolean enableDebug = false;
996
997 private final FlowPanel panel = new FlowPanel();
998
999
1000
1001
1002
1003
1004 public final TextBox tb;
1005
1006
1007 public final SuggestionPopup suggestionPopup;
1008
1009
1010
1011
1012 private final HTML popupOpener = new HTML("") {
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022 @Override
1023 public void onBrowserEvent(Event event) {
1024 super.onBrowserEvent(event);
1025
1026
1027
1028
1029
1030 handleMouseDownEvent(event);
1031 }
1032 };
1033
1034 private final Image selectedItemIcon = new Image();
1035
1036
1037 public ApplicationConnection client;
1038
1039
1040 public String paintableId;
1041
1042
1043 public int currentPage;
1044
1045
1046
1047
1048
1049
1050
1051 public final List<FilterSelectSuggestion> currentSuggestions = new ArrayList<FilterSelectSuggestion>();
1052
1053
1054 public boolean immediate;
1055
1056
1057 public String selectedOptionKey;
1058
1059
1060 public boolean waitingForFilteringResponse = false;
1061
1062
1063 public boolean updateSelectionWhenReponseIsReceived = false;
1064
1065 private boolean tabPressedWhenPopupOpen = false;
1066
1067
1068 public boolean initDone = false;
1069
1070
1071 public String lastFilter = "";
1072
1073
1074 public enum Select {
1075 NONE, FIRST, LAST
1076 };
1077
1078
1079 public Select selectPopupItemWhenResponseIsReceived = Select.NONE;
1080
1081
1082
1083
1084
1085
1086
1087
1088 public FilterSelectSuggestion currentSuggestion;
1089
1090
1091 public boolean allowNewItem;
1092
1093
1094 public int totalMatches;
1095
1096
1097 public boolean nullSelectionAllowed;
1098
1099
1100 public boolean nullSelectItem;
1101
1102
1103 public boolean enabled;
1104
1105
1106 public boolean readonly;
1107
1108
1109 public FilteringMode filteringmode = FilteringMode.OFF;
1110
1111
1112 private static final String CLASSNAME_PROMPT = "prompt";
1113
1114
1115 public String inputPrompt = "";
1116
1117
1118 public boolean prompting = false;
1119
1120
1121
1122
1123
1124
1125
1126
1127 public boolean popupOpenerClicked;
1128
1129
1130 public int suggestionPopupMinWidth = 0;
1131
1132 private int popupWidth = -1;
1133
1134
1135
1136
1137
1138
1139 public String lastNewItemString;
1140
1141
1142 public boolean focused = false;
1143
1144
1145
1146
1147
1148 private boolean textInputEnabled = true;
1149
1150
1151
1152
1153 public VFilterSelectPatched() {
1154 tb = createTextBox();
1155 suggestionPopup = createSuggestionPopup();
1156
1157 selectedItemIcon.setStyleName("v-icon");
1158 selectedItemIcon.addLoadHandler(new LoadHandler() {
1159
1160 @Override
1161 public void onLoad(LoadEvent event) {
1162 if (BrowserInfo.get().isIE8()) {
1163
1164
1165 forceReflow();
1166 }
1167 updateRootWidth();
1168 updateSelectedIconPosition();
1169 }
1170 });
1171
1172 popupOpener.sinkEvents(Event.ONMOUSEDOWN);
1173 Roles.getButtonRole()
1174 .setAriaHiddenState(popupOpener.getElement(), true);
1175 Roles.getButtonRole().set(popupOpener.getElement());
1176
1177 panel.add(tb);
1178 panel.add(popupOpener);
1179 initWidget(panel);
1180 Roles.getComboboxRole().set(panel.getElement());
1181
1182 tb.addKeyDownHandler(this);
1183 tb.addKeyUpHandler(this);
1184
1185 tb.addFocusHandler(this);
1186 tb.addBlurHandler(this);
1187 tb.addClickHandler(this);
1188
1189 popupOpener.addClickHandler(this);
1190
1191 setStyleName(CLASSNAME);
1192 }
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203 protected TextBox createTextBox() {
1204 return new FilterSelectTextBox();
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216 protected SuggestionPopup createSuggestionPopup() {
1217 return new SuggestionPopup();
1218 }
1219
1220 @Override
1221 public void setStyleName(String style) {
1222 super.setStyleName(style);
1223 updateStyleNames();
1224 }
1225
1226 @Override
1227 public void setStylePrimaryName(String style) {
1228 super.setStylePrimaryName(style);
1229 updateStyleNames();
1230 }
1231
1232 protected void updateStyleNames() {
1233 tb.setStyleName(getStylePrimaryName() + "-input");
1234 popupOpener.setStyleName(getStylePrimaryName() + "-button");
1235 suggestionPopup.setStyleName(getStylePrimaryName() + "-suggestpopup");
1236 }
1237
1238
1239
1240
1241
1242
1243
1244 public boolean hasNextPage() {
1245 if (totalMatches > (currentPage + 1) * pageLength) {
1246 return true;
1247 } else {
1248 return false;
1249 }
1250 }
1251
1252
1253
1254
1255
1256
1257
1258
1259 public void filterOptions(int page) {
1260 filterOptions(page, tb.getText());
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271 public void filterOptions(int page, String filter) {
1272 filterOptions(page, filter, true);
1273 }
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285 private void filterOptions(int page, String filter, boolean immediate) {
1286 if (enableDebug) {
1287 debug("VFS: filterOptions(" + page + ", " + filter + ", "
1288 + immediate + ")");
1289 }
1290 if (filter.equals(lastFilter) && currentPage == page) {
1291 if (!suggestionPopup.isAttached()) {
1292 suggestionPopup.showSuggestions(currentSuggestions,
1293 currentPage, totalMatches);
1294 }
1295 return;
1296 }
1297 if (!filter.equals(lastFilter)) {
1298
1299 if ("".equals(filter)) {
1300
1301 page = -1;
1302 } else {
1303 page = 0;
1304 }
1305 }
1306
1307 waitingForFilteringResponse = true;
1308 client.updateVariable(paintableId, "filter", filter, false);
1309 client.updateVariable(paintableId, "page", page, immediate);
1310 lastFilter = filter;
1311 currentPage = page;
1312 }
1313
1314
1315 public void updateReadOnly() {
1316 debug("VFS: updateReadOnly()");
1317 tb.setReadOnly(readonly || !textInputEnabled);
1318 }
1319
1320 public void setTextInputEnabled(boolean textInputEnabled) {
1321 debug("VFS: setTextInputEnabled()");
1322
1323 if (textInputEnabled) {
1324 removeStyleDependentName(STYLE_NO_INPUT);
1325 Roles.getTextboxRole().removeAriaReadonlyProperty(tb.getElement());
1326 } else {
1327 addStyleDependentName(STYLE_NO_INPUT);
1328 Roles.getTextboxRole().setAriaReadonlyProperty(tb.getElement(),
1329 true);
1330 }
1331
1332 if (this.textInputEnabled == textInputEnabled) {
1333 return;
1334 }
1335
1336 this.textInputEnabled = textInputEnabled;
1337 updateReadOnly();
1338 }
1339
1340
1341
1342
1343
1344
1345
1346 public void setTextboxText(final String text) {
1347 if (enableDebug) {
1348 debug("VFS: setTextboxText(" + text + ")");
1349 }
1350 tb.setText(text);
1351 }
1352
1353
1354
1355
1356
1357 public void setPromptingOn() {
1358 debug("VFS: setPromptingOn()");
1359 if (!prompting) {
1360 prompting = true;
1361 addStyleDependentName(CLASSNAME_PROMPT);
1362 }
1363 setTextboxText(inputPrompt);
1364 }
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375 public void setPromptingOff(String text) {
1376 debug("VFS: setPromptingOff()");
1377 setTextboxText(text);
1378 if (prompting) {
1379 prompting = false;
1380 removeStyleDependentName(CLASSNAME_PROMPT);
1381 }
1382 }
1383
1384
1385
1386
1387
1388
1389
1390 public void onSuggestionSelected(FilterSelectSuggestion suggestion) {
1391 if (enableDebug) {
1392 debug("VFS: onSuggestionSelected(" + suggestion.caption + ": "
1393 + suggestion.key + ")");
1394 }
1395 updateSelectionWhenReponseIsReceived = false;
1396
1397 currentSuggestion = suggestion;
1398 String newKey;
1399 if (suggestion.key.equals("")) {
1400
1401 newKey = "";
1402 } else {
1403
1404 newKey = suggestion.getOptionKey();
1405 }
1406
1407 String text = suggestion.getReplacementString();
1408 if ("".equals(newKey) && !focused) {
1409 setPromptingOn();
1410 } else {
1411 setPromptingOff(text);
1412 }
1413 setSelectedItemIcon(suggestion.getIconUri());
1414 if (!(newKey.equals(selectedOptionKey) || ("".equals(newKey) && selectedOptionKey == null))) {
1415 selectedOptionKey = newKey;
1416 client.updateVariable(paintableId, "selected",
1417 new String[] { selectedOptionKey }, immediate);
1418
1419 }
1420 suggestionPopup.hide();
1421 }
1422
1423
1424
1425
1426
1427
1428
1429
1430 public void setSelectedItemIcon(String iconUri) {
1431 if (iconUri == null || iconUri.length() == 0) {
1432 if (selectedItemIcon.isAttached()) {
1433 panel.remove(selectedItemIcon);
1434 if (BrowserInfo.get().isIE8()) {
1435
1436
1437 forceReflow();
1438 }
1439 updateRootWidth();
1440 }
1441 } else {
1442 panel.insert(selectedItemIcon, 0);
1443 selectedItemIcon.setUrl(iconUri);
1444 updateRootWidth();
1445 updateSelectedIconPosition();
1446 }
1447 }
1448
1449 private void forceReflow() {
1450 Util.setStyleTemporarily(tb.getElement(), "zoom", "1");
1451 }
1452
1453
1454
1455
1456
1457 private void updateSelectedIconPosition() {
1458
1459 int availableHeight = 0;
1460 availableHeight = getOffsetHeight();
1461
1462 int iconHeight = Util.getRequiredHeight(selectedItemIcon);
1463 int marginTop = (availableHeight - iconHeight) / 2;
1464 DOM.setStyleAttribute(selectedItemIcon.getElement(), "marginTop",
1465 marginTop + "px");
1466 }
1467
1468 private static Set<Integer> navigationKeyCodes = new HashSet<Integer>();
1469 static {
1470 navigationKeyCodes.add(KeyCodes.KEY_DOWN);
1471 navigationKeyCodes.add(KeyCodes.KEY_UP);
1472 navigationKeyCodes.add(KeyCodes.KEY_PAGEDOWN);
1473 navigationKeyCodes.add(KeyCodes.KEY_PAGEUP);
1474 navigationKeyCodes.add(KeyCodes.KEY_ENTER);
1475 }
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485 @Override
1486 public void onKeyDown(KeyDownEvent event) {
1487 if (enabled && !readonly) {
1488 int keyCode = event.getNativeKeyCode();
1489
1490 if (enableDebug) {
1491 debug("VFS: key down: " + keyCode);
1492 }
1493 if (waitingForFilteringResponse
1494 && navigationKeyCodes.contains(keyCode)) {
1495
1496
1497
1498
1499
1500 if (enableDebug) {
1501 debug("Ignoring "
1502 + keyCode
1503 + " because we are waiting for a filtering response");
1504 }
1505 DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
1506 event.stopPropagation();
1507 return;
1508 }
1509
1510 if (suggestionPopup.isAttached()) {
1511 if (enableDebug) {
1512 debug("Keycode " + keyCode + " target is popup");
1513 }
1514 popupKeyDown(event);
1515 } else {
1516 if (enableDebug) {
1517 debug("Keycode " + keyCode + " target is text field");
1518 }
1519 inputFieldKeyDown(event);
1520 }
1521 }
1522 }
1523
1524 private void debug(String string) {
1525 if (enableDebug) {
1526 VConsole.error(string);
1527 }
1528 }
1529
1530
1531
1532
1533
1534
1535
1536 private void inputFieldKeyDown(KeyDownEvent event) {
1537 if (enableDebug) {
1538 debug("VFS: inputFieldKeyDown(" + event.getNativeKeyCode() + ")");
1539 }
1540 switch (event.getNativeKeyCode()) {
1541 case KeyCodes.KEY_DOWN:
1542 case KeyCodes.KEY_UP:
1543 case KeyCodes.KEY_PAGEDOWN:
1544 case KeyCodes.KEY_PAGEUP:
1545
1546 filterOptions(-1, "");
1547 lastFilter = "";
1548 tb.selectAll();
1549 break;
1550 case KeyCodes.KEY_ENTER:
1551
1552
1553
1554
1555
1556 if (!allowNewItem) {
1557 return;
1558 }
1559
1560 if (currentSuggestion != null
1561 && tb.getText().equals(
1562 currentSuggestion.getReplacementString())) {
1563
1564
1565 return;
1566 }
1567 suggestionPopup.menu.doSelectedItemAction();
1568
1569 event.stopPropagation();
1570 break;
1571 }
1572
1573 }
1574
1575
1576
1577
1578
1579
1580
1581 private void popupKeyDown(KeyDownEvent event) {
1582 if (enableDebug) {
1583 debug("VFS: popupKeyDown(" + event.getNativeKeyCode() + ")");
1584 }
1585
1586
1587 switch (event.getNativeKeyCode()) {
1588 case KeyCodes.KEY_DOWN:
1589 suggestionPopup.selectNextItem();
1590 suggestionPopup.menu.setKeyboardSelectedItem(suggestionPopup.menu
1591 .getSelectedItem());
1592 DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
1593 event.stopPropagation();
1594 break;
1595 case KeyCodes.KEY_UP:
1596 suggestionPopup.selectPrevItem();
1597 suggestionPopup.menu.setKeyboardSelectedItem(suggestionPopup.menu
1598 .getSelectedItem());
1599 DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
1600 event.stopPropagation();
1601 break;
1602 case KeyCodes.KEY_PAGEDOWN:
1603 if (hasNextPage()) {
1604 filterOptions(currentPage + 1, lastFilter);
1605 }
1606 event.stopPropagation();
1607 break;
1608 case KeyCodes.KEY_PAGEUP:
1609 if (currentPage > 0) {
1610 filterOptions(currentPage - 1, lastFilter);
1611 }
1612 event.stopPropagation();
1613 break;
1614 case KeyCodes.KEY_TAB:
1615 tabPressedWhenPopupOpen = true;
1616 filterOptions(currentPage);
1617
1618 break;
1619 case KeyCodes.KEY_ESCAPE:
1620 reset();
1621 DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
1622 event.stopPropagation();
1623 break;
1624 case KeyCodes.KEY_ENTER:
1625 if (suggestionPopup.menu.getKeyboardSelectedItem() == null) {
1626
1627
1628
1629
1630 if (!allowNewItem) {
1631
1632
1633
1634
1635 if (currentSuggestions.size() == 1) {
1636 onSuggestionSelected(currentSuggestions.get(0));
1637 }
1638 } else {
1639
1640 suggestionPopup.menu.doSelectedItemAction();
1641 }
1642 } else {
1643
1644
1645
1646 currentSuggestion = ((FilterSelectSuggestion) suggestionPopup.menu
1647 .getKeyboardSelectedItem().getCommand());
1648 onSuggestionSelected(currentSuggestion);
1649 }
1650
1651 event.stopPropagation();
1652 break;
1653 }
1654
1655 }
1656
1657
1658
1659
1660
1661
1662
1663
1664 @Override
1665 public void onKeyUp(KeyUpEvent event) {
1666 if (enableDebug) {
1667 debug("VFS: onKeyUp(" + event.getNativeKeyCode() + ")");
1668 }
1669 if (enabled && !readonly) {
1670 switch (event.getNativeKeyCode()) {
1671 case KeyCodes.KEY_ENTER:
1672 case KeyCodes.KEY_TAB:
1673 case KeyCodes.KEY_SHIFT:
1674 case KeyCodes.KEY_CTRL:
1675 case KeyCodes.KEY_ALT:
1676 case KeyCodes.KEY_DOWN:
1677 case KeyCodes.KEY_UP:
1678 case KeyCodes.KEY_PAGEDOWN:
1679 case KeyCodes.KEY_PAGEUP:
1680 case KeyCodes.KEY_ESCAPE:
1681 ;
1682 break;
1683 default:
1684 if (textInputEnabled) {
1685 filterOptions(currentPage);
1686 }
1687 break;
1688 }
1689 }
1690 }
1691
1692
1693
1694
1695 private void reset() {
1696 debug("VFS: reset()");
1697 if (currentSuggestion != null) {
1698 String text = currentSuggestion.getReplacementString();
1699 setPromptingOff(text);
1700 selectedOptionKey = currentSuggestion.key;
1701 } else {
1702 if (focused) {
1703 setPromptingOff("");
1704 } else {
1705 setPromptingOn();
1706 }
1707 selectedOptionKey = null;
1708 }
1709 lastFilter = "";
1710 suggestionPopup.hide();
1711 }
1712
1713
1714
1715
1716
1717 @Override
1718 public void onClick(ClickEvent event) {
1719 debug("VFS: onClick()");
1720 if (textInputEnabled
1721 && event.getNativeEvent().getEventTarget().cast() == tb
1722 .getElement()) {
1723
1724 return;
1725 }
1726 if (enabled && !readonly) {
1727
1728
1729 if (!suggestionPopup.isJustClosed()) {
1730
1731
1732
1733 boolean immediate = focused
1734 || !client.hasEventListeners(this, EventId.FOCUS);
1735 filterOptions(-1, "", immediate);
1736 popupOpenerClicked = true;
1737 lastFilter = "";
1738 }
1739 DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
1740 focus();
1741 tb.selectAll();
1742 }
1743 }
1744
1745
1746
1747
1748
1749
1750
1751 public void updateSuggestionPopupMinWidth() {
1752
1753 String captions = Util.escapeHTML(inputPrompt);
1754
1755 for (FilterSelectSuggestion suggestion : currentSuggestions) {
1756
1757
1758 if (captions.length() > 0) {
1759 captions += "|";
1760 }
1761 captions += Util.escapeHTML(suggestion.getReplacementString());
1762 }
1763
1764
1765 suggestionPopupMinWidth = minWidth(captions);
1766 }
1767
1768
1769
1770
1771
1772
1773 public native int minWidth(String captions)
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793 ;
1794
1795
1796
1797
1798 boolean iePreventNextFocus = false;
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808 @Override
1809 public void onFocus(FocusEvent event) {
1810 debug("VFS: onFocus()");
1811
1812
1813
1814
1815
1816
1817 if (BrowserInfo.get().isIE() && iePreventNextFocus) {
1818 iePreventNextFocus = false;
1819 return;
1820 }
1821
1822 focused = true;
1823 if (prompting && !readonly) {
1824 setPromptingOff("");
1825 }
1826 addStyleDependentName("focus");
1827
1828 if (client.hasEventListeners(this, EventId.FOCUS)) {
1829 client.updateVariable(paintableId, EventId.FOCUS, "", true);
1830 }
1831 }
1832
1833
1834
1835
1836
1837 boolean preventNextBlurEventInIE = false;
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847 @Override
1848 public void onBlur(BlurEvent event) {
1849 debug("VFS: onBlur()");
1850
1851 if (BrowserInfo.get().isIE() && preventNextBlurEventInIE) {
1852
1853
1854
1855
1856
1857
1858
1859
1860 preventNextBlurEventInIE = false;
1861
1862 Element focusedElement = Util.getIEFocusedElement();
1863 if (getElement().isOrHasChild(focusedElement)
1864 || suggestionPopup.getElement()
1865 .isOrHasChild(focusedElement)) {
1866
1867
1868
1869
1870 iePreventNextFocus = true;
1871 tb.setFocus(true);
1872 return;
1873 }
1874 }
1875
1876 focused = false;
1877 if (!readonly) {
1878
1879 if (tabPressedWhenPopupOpen) {
1880 tabPressedWhenPopupOpen = false;
1881 suggestionPopup.menu.doSelectedItemAction();
1882 suggestionPopup.hide();
1883 } else if ((!suggestionPopup.isAttached() && waitingForFilteringResponse)
1884 || suggestionPopup.isJustClosed()) {
1885
1886
1887 waitingForFilteringResponse = false;
1888 suggestionPopup.menu.doSelectedItemAction();
1889 }
1890 if (selectedOptionKey == null) {
1891 setPromptingOn();
1892 } else if (currentSuggestion != null) {
1893 setPromptingOff(currentSuggestion.caption);
1894 }
1895 }
1896 removeStyleDependentName("focus");
1897
1898 if (client.hasEventListeners(this, EventId.BLUR)) {
1899 client.updateVariable(paintableId, EventId.BLUR, "", true);
1900 }
1901 }
1902
1903
1904
1905
1906
1907
1908
1909 @Override
1910 public void focus() {
1911 debug("VFS: focus()");
1912 focused = true;
1913 if (prompting && !readonly) {
1914 setPromptingOff("");
1915 }
1916 tb.setFocus(true);
1917 }
1918
1919
1920
1921
1922
1923
1924
1925 public void updateRootWidth() {
1926 ComponentConnector paintable = ConnectorMap.get(client).getConnector(
1927 this);
1928 if (paintable.isUndefinedWidth()) {
1929
1930
1931
1932
1933
1934
1935
1936
1937 int w = Util.getRequiredWidth(this);
1938 if ((!initDone || currentPage + 1 < 0)
1939 && suggestionPopupMinWidth > w) {
1940
1941
1942
1943
1944
1945
1946
1947 Style style = getElement().getStyle();
1948 String originalPadding = style.getPadding();
1949 String originalBorder = style.getBorderWidth();
1950 style.setPaddingLeft(0, Unit.PX);
1951 style.setBorderWidth(0, Unit.PX);
1952 int offset = w - Util.getRequiredWidth(this);
1953 style.setProperty("padding", originalPadding);
1954 style.setProperty("borderWidth", originalBorder);
1955
1956 setWidth(suggestionPopupMinWidth + offset + "px");
1957 }
1958
1959
1960
1961
1962
1963 if (!tb.getElement().getStyle().getWidth().endsWith("px")) {
1964 tb.setWidth((tb.getOffsetWidth() - selectedItemIcon
1965 .getOffsetWidth()) + "px");
1966 }
1967 }
1968 }
1969
1970
1971
1972
1973
1974
1975
1976 private int getMainWidth() {
1977 return getOffsetWidth();
1978 }
1979
1980 @Override
1981 public void setWidth(String width) {
1982 super.setWidth(width);
1983 if (width.length() != 0) {
1984 tb.setWidth("100%");
1985 }
1986 }
1987
1988
1989
1990
1991
1992
1993 private void handleMouseDownEvent(Event event) {
1994
1995
1996
1997
1998 if (event.getTypeInt() == Event.ONMOUSEDOWN) {
1999 event.preventDefault();
2000 event.stopPropagation();
2001
2002
2003
2004
2005
2006
2007
2008
2009 if (BrowserInfo.get().isIE() && focused) {
2010 preventNextBlurEventInIE = true;
2011 debug("VFS: Going to prevent next blur event on IE");
2012 }
2013 }
2014 }
2015
2016 @Override
2017 protected void onDetach() {
2018 super.onDetach();
2019 suggestionPopup.hide();
2020 }
2021
2022 @Override
2023 public Element getSubPartElement(String subPart) {
2024 if ("textbox".equals(subPart)) {
2025 return tb.getElement();
2026 } else if ("button".equals(subPart)) {
2027 return popupOpener.getElement();
2028 }
2029 return null;
2030 }
2031
2032 @Override
2033 public String getSubPartName(Element subElement) {
2034 if (tb.getElement().isOrHasChild(subElement)) {
2035 return "textbox";
2036 } else if (popupOpener.getElement().isOrHasChild(subElement)) {
2037 return "button";
2038 }
2039 return null;
2040 }
2041
2042 @Override
2043 public void setAriaRequired(boolean required) {
2044 AriaHelper.handleInputRequired(tb, required);
2045 }
2046
2047 @Override
2048 public void setAriaInvalid(boolean invalid) {
2049 AriaHelper.handleInputInvalid(tb, invalid);
2050 }
2051
2052 @Override
2053 public void bindAriaCaption(Element captionElement) {
2054 AriaHelper.bindCaption(tb, captionElement);
2055 }
2056 }