Subversion Repositories eFlore/Applications.coel

Rev

Rev 1318 | Rev 1415 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1029 jpm 1
package org.tela_botanica.client.composants;
2
 
3
import java.util.ArrayList;
4
import java.util.Iterator;
5
import java.util.List;
6
 
7
import org.tela_botanica.client.modeles.aDonnee;
8
import org.tela_botanica.client.util.Debug;
9
import org.tela_botanica.client.util.UtilString;
10
 
11
import com.extjs.gxt.ui.client.GXT;
12
import com.extjs.gxt.ui.client.Style.Scroll;
13
import com.extjs.gxt.ui.client.core.El;
14
import com.extjs.gxt.ui.client.core.XDOM;
15
import com.extjs.gxt.ui.client.core.XTemplate;
16
import com.extjs.gxt.ui.client.data.BaseModelData;
17
import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
18
import com.extjs.gxt.ui.client.data.ModelData;
19
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
20
import com.extjs.gxt.ui.client.data.PagingLoader;
21
import com.extjs.gxt.ui.client.event.BaseEvent;
22
import com.extjs.gxt.ui.client.event.ComponentEvent;
23
import com.extjs.gxt.ui.client.event.DomEvent;
24
import com.extjs.gxt.ui.client.event.Events;
25
import com.extjs.gxt.ui.client.event.FieldEvent;
26
import com.extjs.gxt.ui.client.event.ListViewEvent;
27
import com.extjs.gxt.ui.client.event.Listener;
28
import com.extjs.gxt.ui.client.event.PreviewEvent;
29
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
30
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
31
import com.extjs.gxt.ui.client.event.SelectionProvider;
32
import com.extjs.gxt.ui.client.store.ListStore;
33
import com.extjs.gxt.ui.client.store.StoreEvent;
34
import com.extjs.gxt.ui.client.store.StoreListener;
35
import com.extjs.gxt.ui.client.util.BaseEventPreview;
36
import com.extjs.gxt.ui.client.util.DelayedTask;
37
import com.extjs.gxt.ui.client.util.KeyNav;
38
import com.extjs.gxt.ui.client.util.Util;
39
import com.extjs.gxt.ui.client.widget.CheckBoxListView;
40
import com.extjs.gxt.ui.client.widget.ComponentHelper;
41
import com.extjs.gxt.ui.client.widget.LayoutContainer;
42
import com.extjs.gxt.ui.client.widget.ListView;
43
import com.extjs.gxt.ui.client.widget.form.ComboBox;
44
import com.extjs.gxt.ui.client.widget.form.ListModelPropertyEditor;
45
import com.extjs.gxt.ui.client.widget.form.PropertyEditor;
46
import com.extjs.gxt.ui.client.widget.form.TriggerField;
47
import com.extjs.gxt.ui.client.widget.form.ComboBox.ComboBoxMessages;
48
import com.extjs.gxt.ui.client.widget.form.ComboBox.TriggerAction;
49
import com.extjs.gxt.ui.client.widget.form.TextField.TextFieldMessages;
50
import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar;
51
import com.google.gwt.dom.client.Document;
52
import com.google.gwt.dom.client.InputElement;
53
import com.google.gwt.event.dom.client.KeyCodes;
54
import com.google.gwt.user.client.Command;
55
import com.google.gwt.user.client.DeferredCommand;
56
import com.google.gwt.user.client.Element;
57
import com.google.gwt.user.client.Event;
58
import com.google.gwt.user.client.ui.RootPanel;
59
 
60
 
61
public class ChampComboBoxMultiSelect<D extends ModelData> extends TriggerField<D> implements SelectionProvider<D> {
62
 
63
	/**
64
	 * ComboBox error messages.
65
	 */
66
	public class ComboBoxMessages extends TextFieldMessages {
67
 
68
		private String loadingText = GXT.MESSAGES.loadMask_msg();
69
		private String valueNoutFoundText;
70
 
71
		/**
72
		 * Returns the loading text.
73
		 *
74
		 * @return the loading text
75
		 */
76
		public String getLoadingText() {
77
			return loadingText;
78
		}
79
 
80
		/**
81
		 * Returns the value not found error text.
82
		 *
83
		 * @return the error text
84
		 */
85
		public String getValueNoutFoundText() {
86
			return valueNoutFoundText;
87
		}
88
 
89
		/**
90
		 * Sets the loading text.
91
		 *
92
		 * @param loadingText the loading text
93
		 */
94
		public void setLoadingText(String loadingText) {
95
			this.loadingText = loadingText;
96
		}
97
 
98
		/**
99
		 * When using a name/value combo, if the value passed to setValue is not
100
		 * found in the store, valueNotFoundText will be displayed as the field text
101
		 * if defined.
102
		 *
103
		 * @param valueNoutFoundText
104
		 */
105
		public void setValueNoutFoundText(String valueNoutFoundText) {
106
			this.valueNoutFoundText = valueNoutFoundText;
107
		}
108
 
109
	}
110
 
111
	/**
112
	 * TriggerAction enum.
113
	 */
114
	public enum TriggerAction {
115
		ALL, QUERY;
116
	}
117
 
118
	protected boolean autoComplete = false;
119
	protected boolean delayedCheck;
120
	protected String lastQuery;
121
 
122
	protected ListStore<D> store;
123
	private String allQuery = "";
124
	private BaseEventPreview eventPreview;
125
	private boolean expanded;
126
	private El footer;
127
	private boolean forceSelection;
128
	private InputElement hiddenInput;
129
	private String itemSelector;
130
	private String lastSelectionText;
131
	private boolean lazyRender = true, initialized;
132
	private LayoutContainer list;
133
	private String listAlign = "tl-bl?";
134
	private String listStyle = "x-combo-list";
135
	private int maxHeight = 300;
136
	private int minChars = 4;
137
	private int minListWidth = 70;
138
	private String mode = "remote";
139
	private int pageSize;
140
	private PagingToolBar pageTb;
141
	private int queryDelay = 500;
142
	private D selectedItem;
143
	private String selectedStyle = "x-combo-selected";
144
	private StoreListener<D> storeListener;
145
	private DelayedTask taTask, dqTask;
146
	private XTemplate template;
147
	private TriggerAction triggerAction = TriggerAction.QUERY;
148
	private boolean typeAhead;
149
	private int typeAheadDelay = 250;
150
	private boolean useQueryCache = true;
151
	private String valueField;
152
 
153
	//+----------------------------------------------------------------------------------------------------------------+
154
	// Attributs modifiés ou ajoutés
155
	private CheckBoxListView<D> listView = null;
156
	private String valueFieldSeparator = aDonnee.SEPARATEUR_VALEURS;
157
	private String rawSeparator = ", ";
158
	private List<D> listeInitiale = new ArrayList<D>();
159
	private boolean initialisation = false;
160
	private boolean premierAppel = true;
161
	//+----------------------------------------------------------------------------------------------------------------+
162
 
163
	//+----------------------------------------------------------------------------------------------------------------+
164
	// Constructeur modifiés
165
	public ChampComboBoxMultiSelect() {
166
		messages = new ComboBoxMessages();
167
		listView = new CheckBoxListView<D>();
168
		setPropertyEditor(new ListModelPropertyEditor<D>());
169
		monitorWindowResize = true;
170
		windowResizeDelay = 0;
171
		initComponent();
172
		setTriggerAction(TriggerAction.ALL);
173
	}
174
	//+----------------------------------------------------------------------------------------------------------------+
175
 
176
	public void addSelectionChangedListener(SelectionChangedListener<D> listener) {
177
		addListener(Events.SelectionChange, listener);
178
	}
179
 
180
	@Override
181
	public void clear() {
182
		getStore().clearFilters();
183
		boolean f = forceSelection;
184
		forceSelection = false;
185
		super.clear();
186
		forceSelection = f;
187
	}
188
 
189
	/**
190
	 * Clears any text/value currently set in the field.
191
	 */
192
	public void clearSelections() {
193
		setRawValue("");
194
		lastSelectionText = "";
195
		applyEmptyText();
196
		value = null;
197
	}
198
 
199
	/**
200
	 * Execute a query to filter the dropdown list. Fires the BeforeQuery event
201
	 * prior to performing the query allowing the query action to be canceled if
202
	 * needed.
203
	 *
204
	 * @param q the query
205
	 * @param forceAll true to force the query to execute even if there are
206
	 *					currently fewer characters in the field than the minimum specified
207
	 *					by the minChars config option. It also clears any filter
208
	 *					previously saved in the current store
209
	 */
210
	public void doQuery(String q, boolean forceAll) {
211
		if (q == null) {
212
			q = "";
213
		}
214
 
215
		FieldEvent fe = new FieldEvent(this);
216
		fe.setValue(q);
217
		if (!fireEvent(Events.BeforeQuery, fe)) {
218
			return;
219
		}
220
 
221
		if (forceAll || q.length() >= minChars) {
222
			if (!useQueryCache || !q.equals(lastQuery)) {
223
				lastQuery = q;
224
				if (mode.equals("local")) {
225
					selectedItem = null;
226
					store.filter(getDisplayField(), q);
227
					onLoad(null);
228
				} else {
229
					expand();
230
					store.getLoader().load(getParams(q));
231
				}
232
			} else {
233
				selectedItem = null;
234
				onLoad(null);
235
			}
236
		}
237
	}
238
 
239
	/**
240
	 * Returns the all query.
241
	 *
242
	 * @return the all query
243
	 */
244
	public String getAllQuery() {
245
		return allQuery;
246
	}
247
 
248
	/**
249
	 * Returns the display field.
250
	 *
251
	 * @return the display field
252
	 */
253
	public String getDisplayField() {
254
		return getPropertyEditor().getDisplayProperty();
255
	}
256
 
257
	/**
258
	 * Returns true if the field's value is forced to one of the value in the
259
	 * list.
260
	 *
261
	 * @return the force selection state
262
	 */
263
	public boolean getForceSelection() {
264
		return forceSelection;
265
	}
266
 
267
	/**
268
	 * Returns the item selector.
269
	 *
270
	 * @return the item selector
271
	 */
272
	public String getItemSelector() {
273
		return itemSelector;
274
	}
275
 
276
	/**
277
	 * Returns the list's list align value.
278
	 *
279
	 * @return the list align value
280
	 */
281
	public String getListAlign() {
282
		return listAlign;
283
	}
284
 
285
	/**
286
	 * Returns the list style.
287
	 *
288
	 * @return the list style
289
	 */
290
	public String getListStyle() {
291
		return listStyle;
292
	}
293
 
294
	/**
295
	 * Returns the loading text.
296
	 *
297
	 * @return the loading text
298
	 */
299
	public String getLoadingText() {
300
		return getMessages().getLoadingText();
301
	}
302
 
303
	/**
304
	 * Returns the dropdown list's max height.
305
	 *
306
	 * @return the max height
307
	 */
308
	public int getMaxHeight() {
309
		return maxHeight;
310
	}
311
 
312
	@Override
313
	public ComboBoxMessages getMessages() {
314
		return (ComboBoxMessages) messages;
315
	}
316
 
317
	/**
318
	 * Returns the min characters used for autocompete and typeahead.
319
	 *
320
	 * @return the minimum number of characters
321
	 */
322
	public int getMinChars() {
323
		return minChars;
324
	}
325
 
326
	/**
327
	 * Returns the dropdown list's min width.
328
	 *
329
	 * @return the min width
330
	 */
331
	public int getMinListWidth() {
332
		return minListWidth;
333
	}
334
 
335
	/**
336
	 * Returns the page size.
337
	 *
338
	 * @return the page size
339
	 */
340
	public int getPageSize() {
341
		return pageSize;
342
	}
343
 
344
	/**
345
	 * Returns the combo's paging tool bar.
346
	 *
347
	 * @return the tool bar
348
	 */
349
	public PagingToolBar getPagingToolBar() {
350
		return pageTb;
351
	}
352
 
353
	@Override
354
	public ListModelPropertyEditor<D> getPropertyEditor() {
355
		return (ListModelPropertyEditor<D>) propertyEditor;
356
	}
357
 
358
	/**
359
	 * Returns the query delay.
360
	 *
361
	 * @return the query delay
362
	 */
363
	public int getQueryDelay() {
364
		return queryDelay;
365
	}
366
 
367
	/**
368
	 * Returns the selected style.
369
	 *
370
	 * @return the selected style
371
	 */
372
	public String getSelectedStyle() {
373
		return selectedStyle;
374
	}
375
 
376
 
377
	/**
378
	 * Returns the combo's store.
379
	 *
380
	 * @return the store
381
	 */
382
	public ListStore<D> getStore() {
383
		return store;
384
	}
385
 
386
	/**
387
	 * Returns the custom template.
388
	 *
389
	 * @return the template
390
	 */
391
	public XTemplate getTemplate() {
392
		return template;
393
	}
394
 
395
	/**
396
	 * Returns the trigger action.
397
	 *
398
	 * @return the trigger action
399
	 */
400
	public TriggerAction getTriggerAction() {
401
		return triggerAction;
402
	}
403
 
404
	/**
405
	 * Returns the type ahead delay in milliseconds.
406
	 *
407
	 * @return the type ahead delay
408
	 */
409
	public int getTypeAheadDelay() {
410
		return typeAheadDelay;
411
	}
412
 
413
	/**
414
	 * Returns the value field name.
415
	 *
416
	 * @return the value field name
417
	 */
418
	public String getValueField() {
419
		return valueField;
420
	}
421
 
422
 
423
 
424
	/**
425
	 * Returns <code>true</code> if the panel is expanded.
426
	 *
427
	 * @return the expand state
428
	 */
429
	public boolean isExpanded() {
430
		return expanded;
431
	}
432
 
433
	/**
434
	 * Returns true if lazy rendering is enabled.
435
	 *
436
	 * @return true of lazy rendering
437
	 */
438
	public boolean isLazyRender() {
439
		return lazyRender;
440
	}
441
 
442
	/**
443
	 * Returns true if type ahead is enabled.
444
	 *
445
	 * @return the type ahead state
446
	 */
447
	public boolean isTypeAhead() {
448
		return typeAhead;
449
	}
450
 
451
	/**
452
	 * Returns the state if the query cache is used or not.
453
	 *
454
	 * @return the useQueryCache state
455
	 */
456
	public boolean isUseQueryCache() {
457
		return useQueryCache;
458
	}
459
 
460
	public void removeSelectionListener(SelectionChangedListener<D> listener) {
461
		removeListener(Events.SelectionChange, listener);
462
	}
463
 
464
	@Override
465
	public void reset() {
466
		getStore().clearFilters();
467
		boolean f = forceSelection;
468
		forceSelection = false;
469
		super.reset();
470
		forceSelection = f;
471
	}
472
 
473
	public void select(D sel) {
474
		if (listView != null && sel != null) {
475
			int index = store.indexOf(sel);
476
			selectedItem = sel;
477
			if (index < listView.getElements().size()) {
478
				listView.getSelectionModel().select(sel, false);
479
				fly(listView.getElement(index)).scrollIntoView(listView.getElement(), false);
480
			}
481
		}
482
	}
483
 
484
	/**
485
	 * Select an item in the dropdown list by its numeric index in the list. This
486
	 * function does NOT cause the select event to fire. The list must expanded
487
	 * for this function to work, otherwise use #setValue.
488
	 *
489
	 * @param index the index of the item to select
490
	 */
491
	public void select(int index) {
492
		select(store.getAt(index));
493
	}
494
 
495
	/**
496
	 * The text query to send to the server to return all records for the list
497
	 * with no filtering (defaults to '').
498
	 *
499
	 * @param allQuery the all query
500
	 */
501
	public void setAllQuery(String allQuery) {
502
		this.allQuery = allQuery;
503
	}
504
 
505
	/**
506
	 * The underlying data field name to bind to this ComboBox (defaults to
507
	 * 'text').
508
	 *
509
	 * @param displayField the display field
510
	 */
511
	public void setDisplayField(String displayField) {
512
		getPropertyEditor().setDisplayProperty(displayField);
513
	}
514
 
515
	/**
516
	 * Sets the panel's expand state.
517
	 *
518
	 * @param expand <code>true<code> true to expand
519
	 */
520
	public void setExpanded(boolean expand) {
521
		this.expanded = expand;
522
		if (isRendered()) {
523
			if (expand) {
524
				expand();
525
			} else {
526
				collapse();
527
			}
528
		}
529
	}
530
 
531
	/**
532
	 * Sets whether the combo's value is restricted to one of the values in the
533
	 * list, false to allow the user to set arbitrary text into the field
534
	 * (defaults to false).
535
	 *
536
	 * @param forceSelection true to force selection
537
	 */
538
	public void setForceSelection(boolean forceSelection) {
539
		this.forceSelection = forceSelection;
540
	}
541
 
542
	/**
543
	 * This setting is required if a custom XTemplate has been specified.
544
	 *
545
	 * @param itemSelector the item selector
546
	 */
547
	public void setItemSelector(String itemSelector) {
548
		this.itemSelector = itemSelector;
549
	}
550
 
551
	/**
552
	 * True to lazily render the combo's drop down list (default to true,
553
	 * pre-render).
554
	 *
555
	 * @param lazyRender true to lazy render the drop down list
556
	 */
557
	public void setLazyRender(boolean lazyRender) {
558
		this.lazyRender = lazyRender;
559
	}
560
 
561
	/**
562
	 * Sets a valid anchor position value. See {@link El#alignTo} for details on
563
	 * supported anchor positions (defaults to 'tl-bl?').
564
	 *
565
	 * @param listAlign the new list align value
566
	 */
567
	public void setListAlign(String listAlign) {
568
		this.listAlign = listAlign;
569
	}
570
 
571
	/**
572
	 * Sets the style for the drop down list (defaults to 'x-combo-list');
573
	 *
574
	 * @param listStyle the list style
575
	 */
576
	public void setListStyle(String listStyle) {
577
		this.listStyle = listStyle;
578
	}
579
 
580
	/**
581
	 * Sets the loading text.
582
	 *
583
	 * @param loadingText the loading text
584
	 */
585
	public void setLoadingText(String loadingText) {
586
		getMessages().setLoadingText(loadingText);
587
	}
588
 
589
	/**
590
	 * Sets the maximum height in pixels of the dropdown list before scrollbars
591
	 * are shown (defaults to 300).
592
	 *
593
	 * @param maxHeight the max hieght
594
	 */
595
	public void setMaxHeight(int maxHeight) {
596
		this.maxHeight = maxHeight;
597
	}
598
 
599
	/**
600
	 * Sets the minimum number of characters the user must type before
601
	 * autocomplete and typeahead active (defaults to 4 if remote, or 0 if local).
602
	 *
603
	 * @param minChars
604
	 */
605
	public void setMinChars(int minChars) {
606
		this.minChars = minChars;
607
	}
608
 
609
	/**
610
	 * Sets the minimum width of the dropdown list in pixels (defaults to 70, will
611
	 * be ignored if listWidth has a higher value).
612
	 *
613
	 * @param minListWidth the min width
614
	 */
615
	public void setMinListWidth(int minListWidth) {
616
		this.minListWidth = minListWidth;
617
	}
618
 
619
	/**
620
	 * Sets the page size. Only applies when using a paging toolbar.
621
	 *
622
	 * @param pageSize the page size
623
	 */
624
	public void setPageSize(int pageSize) {
625
		assertPreRender();
626
		this.pageSize = pageSize;
627
		if (pageSize > 0) {
628
			if (pageTb != null) {
629
				pageTb.setPageSize(pageSize);
630
			} else {
631
				pageTb = new PagingToolBar(pageSize);
632
			}
633
		} else {
634
			pageTb = null;
635
		}
636
	}
637
 
638
	@Override
639
	public void setPropertyEditor(PropertyEditor<D> propertyEditor) {
640
		assert propertyEditor instanceof ListModelPropertyEditor<?> : "PropertyEditor must be a ListModelPropertyEditor instance";
641
		super.setPropertyEditor(propertyEditor);
642
	}
643
 
644
	/**
645
	 * The length of time in milliseconds to delay between the start of typing and
646
	 * sending the query to filter the dropdown list.
647
	 *
648
	 * @param queryDelay the query delay
649
	 */
650
	public void setQueryDelay(int queryDelay) {
651
		this.queryDelay = queryDelay;
652
	}
653
 
654
	@Override
655
	public void setRawValue(String text) {
656
		if (rendered) {
657
			if (text == null) {
658
				String msg = getMessages().getValueNoutFoundText();
659
				text = msg != null ? msg : "";
660
			}
661
			getInputEl().setValue(text);
662
		}
663
	}
664
 
665
	/**
666
	 * Sets the CSS style name to apply to the selected item in the dropdown list
667
	 * (defaults to 'x-combo-selected').
668
	 *
669
	 * @param selectedStyle the selected style
670
	 */
671
	public void setSelectedStyle(String selectedStyle) {
672
		this.selectedStyle = selectedStyle;
673
	}
674
 
675
 /**
676
	 * Sets the template fragment to be used for the text of each combo list item.
677
	 *
678
	 * <pre>
679
	 *
680
	 * &lt;code&gt; combo.setSimpleTemplate(&quot;{abbr} {name}&quot;); &lt;/code&gt;
681
	 *
682
	 * </pre>
683
	 *
684
	 * @param html the html used only for the text of each item in the list
685
	 */
686
	public void setSimpleTemplate(String html) {
687
		assertPreRender();
688
		html = "<tpl for=\".\"><div class=x-combo-list-item>" + html + "</div></tpl>";
689
		template = XTemplate.create(html);
690
	}
691
 
692
	/**
693
	 * Sets the combo's store.
694
	 *
695
	 * @param store the store
696
	 */
697
	public void setStore(ListStore<D> store) {
698
		this.store = store;
699
	}
700
 
701
	/**
702
	 * Sets the custom template used to render the combo's drop down list.Use this
703
	 * to create custom UI layouts for items in the list.
704
	 * <p>
705
	 * If you wish to preserve the default visual look of list items, add the CSS
706
	 * class name 'x-combo-list-item' to the template's container element.
707
	 *
708
	 * @param html the html
709
	 */
710
	public void setTemplate(String html) {
711
		assertPreRender();
712
		template = XTemplate.create(html);
713
	}
714
 
715
	/**
716
	 * Sets the custom template used to render the combo's drop down list.
717
	 *
718
	 * @param template the template
719
	 */
720
	public void setTemplate(XTemplate template) {
721
		assertPreRender();
722
		this.template = template;
723
	}
724
 
725
	/**
726
	 * The action to execute when the trigger field is activated. Use
727
	 * {@link TriggerAction#ALL} to run the query specified by the allQuery config
728
	 * option (defaults to {@link TriggerAction#QUERY}).
729
	 *
730
	 * @param triggerAction the trigger action
731
	 */
732
	public void setTriggerAction(TriggerAction triggerAction) {
733
		this.triggerAction = triggerAction;
734
	}
735
 
736
	/**
737
	 * True to populate and autoselect the remainder of the text being typed after
738
	 * a configurable delay ({@link #typeAheadDelay}) if it matches a known value
739
	 * (defaults to false)
740
	 *
741
	 * @param typeAhead
742
	 */
743
	public void setTypeAhead(boolean typeAhead) {
744
		this.typeAhead = typeAhead;
745
		if (rendered) {
746
			if (typeAhead && taTask == null) {
747
				taTask = new DelayedTask(new Listener<BaseEvent>() {
748
					public void handleEvent(BaseEvent be) {
749
						onTypeAhead();
750
					}
751
				});
752
			} else if (!typeAhead && taTask != null) {
753
				taTask.cancel();
754
				taTask = null;
755
			}
756
		}
757
	}
758
 
759
	/**
760
	 * The length of time in milliseconds to wait until the typeahead text is
761
	 * displayed if typeAhead = true (defaults to 250).
762
	 *
763
	 * @param typeAheadDelay the type ahead delay
764
	 */
765
	public void setTypeAheadDelay(int typeAheadDelay) {
766
		this.typeAheadDelay = typeAheadDelay;
767
	}
768
 
769
	/**
770
	 * Set this to false to disable the last query cache (defaults to true).
771
	 *
772
	 * When set to false the store gets queried on each expand for the data that
773
	 * should get displayed in the list. If you use a loader, than each time the
774
	 * ComboBox gets expanded, the server gets asked for the data.
775
	 *
776
	 * You want to do this for example, if you filter the content of this ComboBox
777
	 * against some selection in another field.
778
	 *
779
	 * @param useQueryCache the useQueryCache to set
780
	 */
781
	public void setUseQueryCache(boolean useQueryCache) {
782
		this.useQueryCache = useQueryCache;
783
	}
784
 
785
	/**
786
	 * Sets the model field used to retrieve the "value" from the model. If
787
	 * specified, a hidden form field will contain the value. The hidden form
788
	 * field name will be the combo's field name plus "-hidden".
789
	 *
790
	 * @param valueField the value field name
791
	 */
792
	public void setValueField(String valueField) {
793
		this.valueField = valueField;
794
	}
795
 
796
	protected void collapseIf(PreviewEvent pe) {
797
		if (!list.el().isOrHasChild(pe.getTarget()) && !el().isOrHasChild(pe.getTarget())) {
798
			collapse();
799
		}
800
	}
801
 
802
	protected D findModel(String property, String value) {
803
		if (value == null) return null;
804
		for (D model : store.getModels()) {
805
			if (value.equals(getPropertyEditor().getStringValue(model))) {
806
				return model;
807
			}
808
		}
809
		return null;
810
	}
811
 
812
	protected void fireKey(FieldEvent fe) {
813
		if (fe.isNavKeyPress() && !isExpanded() && !delayedCheck) {
814
			fireEvent(Events.SpecialKey, fe);
815
		}
816
	}
817
 
818
	@Override
819
	protected El getFocusEl() {
820
		return input;
821
	}
822
 
823
	protected PagingLoadConfig getParams(String query) {
824
		BasePagingLoadConfig config = new BasePagingLoadConfig();
825
		config.setLimit(pageSize);
826
		config.setOffset(0);
827
		config.set("query", query);
828
		return config;
829
	}
830
 
831
	protected boolean hasFocus() {
832
		return hasFocus || expanded;
833
	}
834
 
835
	@SuppressWarnings("unchecked")
836
	protected void initComponent() {
837
		storeListener = new StoreListener<D>() {
838
 
839
			@Override
840
			public void storeBeforeDataChanged(StoreEvent<D> se) {
841
				onBeforeLoad(se);
842
			}
843
 
844
			@Override
845
			public void storeDataChanged(StoreEvent<D> se) {
846
				onLoad(se);
847
			}
848
 
849
		};
850
 
851
		eventPreview = new BaseEventPreview() {
852
			@Override
853
			protected boolean onPreview(PreviewEvent pe) {
854
				switch (pe.getType().getEventCode()) {
855
					case Event.ONSCROLL:
856
					case Event.ONMOUSEWHEEL:
857
					case Event.ONMOUSEDOWN:
858
						collapseIf(pe);
859
				}
860
				return true;
861
			}
862
		};
863
		eventPreview.setAutoHide(false);
864
 
865
		new KeyNav(this) {
866
			public void onDown(ComponentEvent ce) {
867
				ce.cancelBubble();
868
				if (!isExpanded()) {
869
					onTriggerClick(ce);
870
				} else {
871
					selectNext();
872
				}
873
			}
874
 
875
			@Override
876
			public void onEnter(ComponentEvent ce) {
877
				if (expanded) {
878
					ce.cancelBubble();
879
					onViewClick(ce, false);
880
					delayedCheck = true;
881
					unsetDelayCheck();
882
				}
883
			}
884
 
885
			@Override
886
			public void onEsc(ComponentEvent ce) {
887
				if (expanded) {
888
					ce.cancelBubble();
889
					collapse();
890
				}
891
			}
892
 
893
			@Override
894
			public void onTab(ComponentEvent ce) {
895
				if (expanded) {
896
					onViewClick(ce, false);
897
				}
898
			}
899
 
900
			@Override
901
			public void onUp(ComponentEvent ce) {
902
				if (expanded) {
903
					ce.cancelBubble();
904
					selectPrev();
905
				}
906
			}
907
 
908
		};
909
	}
910
 
911
	protected void onBeforeLoad(StoreEvent<D> se) {
912
		if (!hasFocus()) {
913
			return;
914
		}
915
		if (expanded) {
916
			restrict();
917
		}
918
	}
919
 
920
	@Override
921
	protected void onDetach() {
922
		collapse();
923
		super.onDetach();
924
		if (eventPreview != null) {
925
			eventPreview.remove();
926
		}
927
	}
928
 
929
	protected void onEmptyResults() {
930
		collapse();
931
	}
932
 
933
	@Override
934
	protected void onKeyDown(FieldEvent fe) {
935
		if (fe.getKeyCode() == KeyCodes.KEY_TAB) {
936
			if (expanded) {
937
				onViewClick(fe, false);
938
			}
939
		}
940
		super.onKeyDown(fe);
941
	}
942
 
943
	@Override
944
	protected void onKeyUp(FieldEvent fe) {
945
		super.onKeyUp(fe);
946
		if (isEditable() && (!fe.isSpecialKey() || fe.getKeyCode() == KeyCodes.KEY_BACKSPACE || fe.getKeyCode() == 46)) {
947
			// last key
948
			dqTask.delay(queryDelay);
949
		}
950
	}
951
 
952
	protected void onLoad(StoreEvent<D> se) {
953
		if (!isAttached() || !hasFocus()) {
954
			return;
955
		}
956
		if (store.getCount() > 0) {
957
			if (expanded) {
958
				restrict();
959
			} else {
960
				expand();
961
			}
962
 
963
			if (lastQuery != null && lastQuery.equals(allQuery)) {
964
				if (isEditable()) {
965
					selectAll();
966
				}
967
			} else {
968
				if (typeAhead) {
969
					taTask.delay(typeAheadDelay);
970
				}
971
			}
972
			if (!selectByValue(getRawValue())) {
973
				select(0);
974
			}
975
		} else {
976
			onEmptyResults();
977
		}
978
	}
979
 
980
	protected void onRender(Element parent, int index) {
981
		super.onRender(parent, index);
982
		initList();
983
 
984
		if (!autoComplete) {
985
			getInputEl().dom.setAttribute("autocomplete", "off");
986
		}
987
 
988
		if (mode.equals("local")) {
989
			minChars = 0;
990
		}
991
 
992
		dqTask = new DelayedTask(new Listener<BaseEvent>() {
993
			public void handleEvent(BaseEvent be) {
994
				initQuery();
995
			}
996
		});
997
 
998
		if (valueField != null) {
999
			hiddenInput = Document.get().createHiddenInputElement().cast();
1000
			hiddenInput.setName(getName() + "-hidden");
1001
			getElement().appendChild(hiddenInput);
1002
		}
1003
 
1004
		if (typeAhead) {
1005
			taTask = new DelayedTask(new Listener<BaseEvent>() {
1006
				public void handleEvent(BaseEvent be) {
1007
					onTypeAhead();
1008
				}
1009
			});
1010
		}
1011
		eventPreview.getIgnoreList().add(getElement());
1012
	}
1013
 
1014
	protected void onSelect(D model, int index) {
1015
		FieldEvent fe = new FieldEvent(this);
1016
		if (fireEvent(Events.BeforeSelect, fe)) {
1017
			setValue(model);
1018
			collapse();
1019
			fireEvent(Events.Select, fe);
1020
		}
1021
	}
1022
 
1023
	protected void onTriggerClick(ComponentEvent ce) {
1024
		super.onTriggerClick(ce);
1025
		if (expanded) {
1026
			collapse();
1027
		} else {
1028
			onFocus(null);
1029
			if (triggerAction == TriggerAction.ALL) {
1030
				doQuery(allQuery, true);
1031
			} else {
1032
				doQuery(getRawValue(), true);
1033
			}
1034
 
1035
		}
1036
		getInputEl().focus();
1037
	}
1038
 
1039
	protected void onTypeAhead() {
1040
		if (store.getCount() > 0) {
1041
			D m = store.getAt(0);
1042
			String newValue = propertyEditor.getStringValue(m);
1043
			int len = newValue.length();
1044
			int selStart = getRawValue().length();
1045
			if (selStart != len) {
1046
				setRawValue(newValue);
1047
				select(selStart, newValue.length());
1048
			}
1049
		}
1050
	}
1051
 
1052
	protected void onViewClick(DomEvent de, boolean focus) {
1053
		int idx = -1;
1054
		// when testing in selenium the items will not be selected as the mouse
1055
		// is not moved during the test
1056
		Element elem = listView.findElement(de.getTarget());
1057
		if (elem != null) {
1058
			idx = listView.indexOf(elem);
1059
		} else {
1060
			D sel = listView.getSelectionModel().getSelectedItem();
1061
			if (sel != null) {
1062
				idx = store.indexOf(sel);
1063
			}
1064
		}
1065
		if (idx != -1) {
1066
			D sel = store.getAt(idx);
1067
			onSelect(sel, idx);
1068
		}
1069
 
1070
		if (focus) {
1071
			DeferredCommand.addCommand(new Command() {
1072
				public void execute() {
1073
					focus();
1074
				}
1075
			});
1076
		}
1077
	}
1078
 
1079
	protected void onWindowResize(int width, int height) {
1080
		collapse();
1081
	}
1082
 
1083
	@Override
1084
	protected void triggerBlur(ComponentEvent ce) {
1085
		doForce();
1086
		super.triggerBlur(ce);
1087
	}
1088
 
1089
	protected void unsetDelayCheck() {
1090
		DeferredCommand.addCommand(new Command() {
1091
			public void execute() {
1092
				delayedCheck = false;
1093
			}
1094
		});
1095
	}
1096
 
1097
	@Override
1098
	protected boolean validateBlur(DomEvent e, Element target) {
1099
		return list == null || (list != null && !list.isVisible() && !list.getElement().isOrHasChild(target));
1100
	}
1101
 
1102
	@Override
1103
	protected boolean validateValue(String value) {
1104
		if (forceSelection) {
1105
			boolean f = forceSelection;
1106
			forceSelection = false;
1107
			if (getValue() == null) {
1108
				forceSelection = f;
1109
				String rv = getRawValue();
1110
				if (getAllowBlank() && (rv == null || rv.equals(""))) {
1111
					return true;
1112
				}
1113
				markInvalid(getMessages().getBlankText());
1114
				return false;
1115
			}
1116
			forceSelection = f;
1117
		}
1118
		return super.validateValue(value);
1119
	}
1120
 
1121
	private void createList(boolean remove) {
1122
		RootPanel.get().add(list);
1123
		initialized = true;
1124
		if (remove) {
1125
			RootPanel.get().remove(list);
1126
		}
1127
	}
1128
 
1129
	private void initQuery() {
1130
		doQuery(getRawValue(), false);
1131
	}
1132
 
1133
	private void restrict() {
1134
		list.el().setVisibility(false);
1135
		listView.setHeight("auto");
1136
		list.setHeight("auto");
1137
		int w = Math.max(getWidth(), minListWidth);
1138
 
1139
		int fh = footer != null ? footer.getHeight() : 0;
1140
		int fw = list.el().getFrameWidth("tb") + fh;
1141
 
1142
		int h = listView.getHeight() + fw;
1143
 
1144
		h = Math.min(h, maxHeight - fw);
1145
		list.setSize(w, h);
1146
		list.el().makePositionable(true);
1147
		list.el().alignTo(getElement(), listAlign, null);
1148
 
1149
		h -= fh;
1150
 
1151
		int width = w - list.el().getFrameWidth("lr");
1152
		listView.syncSize();
1153
		listView.setSize(width, h - list.el().getFrameWidth("tb"));
1154
 
1155
		if (pageTb != null) {
1156
			pageTb.setWidth(width);
1157
		}
1158
 
1159
		int y = list.el().getY();
1160
		int b = y + h;
1161
		int vh = XDOM.getViewportSize().height + XDOM.getBodyScrollTop();
1162
		if (b > vh) {
1163
			y = y - (b - vh) - 5;
1164
			list.el().setTop(y);
1165
		}
1166
		list.el().setVisibility(true);
1167
	}
1168
 
1169
	private boolean selectByValue(String value) {
1170
		D r = findModel(getDisplayField(), value);
1171
		if (r != null) {
1172
			select(r);
1173
			return true;
1174
		}
1175
		return false;
1176
	}
1177
 
1178
	private void selectNext() {
1179
		int count = store.getCount();
1180
		if (count > 0) {
1181
			int selectedIndex = store.indexOf(selectedItem);
1182
			if (selectedIndex == -1) {
1183
				select(0);
1184
			} else if (selectedIndex < count - 1) {
1185
				select(selectedIndex + 1);
1186
			}
1187
		}
1188
	}
1189
 
1190
	private void selectPrev() {
1191
		int count = store.getCount();
1192
		if (count > 0) {
1193
			int selectedIndex = store.indexOf(selectedItem);
1194
			if (selectedIndex == -1) {
1195
				select(0);
1196
			} else if (selectedIndex != 0) {
1197
				select(selectedIndex - 1);
1198
			}
1199
		}
1200
	}
1201
 
1202
//+--------------------------------------------------------------------------------------------------------------------+
1203
// Méthode modifiées
1204
 
1205
	@Override
1206
	public D getValue() {
1207
		return (D) new BaseModelData();
1208
	}
1209
 
1210
	@Override
1211
	public void setValue(D value) {
1212
 
1213
	}
1214
 
1215
	public List<D> getSelection() {
1216
		List<D> sel = new ArrayList<D>();
1217
		if (listeInitiale != null && initialisation == false) {
1218
			sel = listeInitiale;
1219
			if (listView.isRendered()) {
1220
				initialisation = true;
1221
			}
1222
		} else if (listView.isRendered()) {
1223
			sel = listView.getChecked();
1224
		}
1225
		return sel;
1226
	}
1227
 
1228
	public void setSelection(List<D> selection) {
1229
		nettoyerListeDeCasesACocher();
1230
		for (D d : selection) {
1231
			listView.setChecked(d, true);
1232
		}
1233
	}
1234
 
1235
	/**
1236
	 * Returns the combo's list view.
1237
	 *
1238
	 * @return the view
1239
	 */
1240
	public CheckBoxListView<D> getListView() {
1241
		return listView;
1242
	}
1243
 
1244
	/**
1245
	 * Returns the combo's list view.
1246
	 *
1247
	 * @return the view
1248
	 */
1249
	public CheckBoxListView<D> getView() {
1250
		return listView;
1251
	}
1252
 
1253
	/**
1254
	 * Sets the combo's view.
1255
	 *
1256
	 * @param view the view
1257
	 */
1258
	public void setView(CheckBoxListView<D> view) {
1259
		this.listView = view;
1260
	}
1261
 
1262
	public String getRawSeparator() {
1263
		return rawSeparator;
1264
	}
1265
	public void setRawSeparator(String rawSeparator) {
1266
		this.rawSeparator = rawSeparator;
1267
	}
1268
 
1269
	public void setValueFieldSeparator(String valueFieldSeparator) {
1270
		this.valueFieldSeparator = valueFieldSeparator;
1271
	}
1272
	public String getValueFieldSeparator() {
1273
		return valueFieldSeparator;
1274
	}
1275
 
1276
	/**
1277
	 * Hides the dropdown list if it is currently expanded. Fires the
1278
	 * <i>Collapse</i> event on completion.
1279
	 */
1280
	public void collapse() {
1281
		if (!expanded) {
1282
			return;
1283
		}
1284
		eventPreview.remove();
1285
		expanded = false;
1286
		list.hide();
1287
		RootPanel.get().remove(list);
1288
		fireEvent(Events.Collapse, new FieldEvent(this));
1289
		mettreAJour("collapse");
1290
	}
1291
 
1292
	/**
1293
	 * Expands the dropdown list if it is currently hidden. Fires the
1294
	 * <i>expand</i> event on completion.
1295
	 */
1296
	public void expand() {
1297
		if (expanded || !hasFocus) {
1298
			return;
1299
		}
1300
		expanded = true;
1301
 
1302
		if (!initialized) {
1303
			createList(false);
1304
		} else {
1305
			RootPanel.get().add(list);
1306
		}
1307
 
1308
		list.show();
1309
		list.layout();
1310
		list.el().updateZIndex(0);
1311
		restrict();
1312
 
1313
		eventPreview.add();
1314
 
1315
		fireEvent(Events.Expand, new FieldEvent(this));
1316
 
1317
		if (premierAppel) {
1318
			premierAppel = false;
1319
			collapse();
1320
			expand();
1321
		}
1322
	}
1323
 
1324
	protected void initList() {
1325
 
1326
		if (listView == null) {
1327
			setView(new CheckBoxListView<D>());
1328
		}
1329
 
1330
		String style = getListStyle();
1331
		listView.setStyleAttribute("overflowX", "hidden");
1332
		listView.setStyleName(style + "-inner");
1333
		listView.setStyleAttribute("padding", "0px");
1334
		listView.setSelectOnOver(true);
1335
		listView.setBorders(false);
1336
		listView.setStyleAttribute("backgroundColor", "white");
1337
		listView.setSelectStyle(getSelectedStyle());
1338
		listView.setLoadingText(getLoadingText());
1339
 
1340
		if (getTemplate() == null) {
1341
			listView.setDisplayProperty(getDisplayField());
1342
		} else {
1343
			listView.setTemplate(getTemplate());
1344
		}
1345
 
1346
		setMaxHeight(0);
1347
 
1348
		list = new LayoutContainer() {
1349
			@Override
1350
			protected void onRender(Element parent, int index) {
1351
				super.onRender(parent, index);
1352
				eventPreview.getIgnoreList().add(getElement());
1353
			}
1354
		};
1355
		list.setScrollMode(Scroll.NONE);
1356
		list.setShim(true);
1357
		list.setShadow(true);
1358
		list.setBorders(true);
1359
		list.setStyleName(style);
1360
		list.hide();
1361
 
1362
		assert store != null : "ComboBox needs a store";
1363
 
1364
 
1365
		list.add(listView);
1366
		listView.show();
1367
 
1368
		if (!lazyRender) {
1369
			createList(true);
1370
		}
1371
 
1372
		bindStore(store, true);
1373
	};
1374
 
1375
	protected void doForce() {}
1376
 
1377
	private void bindStore(ListStore<D> store, boolean initial) {
1378
		if (this.store != null && !initial) {
1379
			this.store.removeStoreListener(storeListener);
1380
			if (store == null) {
1381
				this.store = null;
1382
				if (listView != null) {
1383
					listView.setStore(null);
1384
				}
1385
			}
1386
		}
1387
		if (store != null) {
1388
			this.store = store;
1389
			if (store.getLoader() == null) {
1390
				mode = "local";
1391
			}
1392
			if (listView != null) {
1393
				listView.setStore(store);
1394
			}
1395
			store.addStoreListener(storeListener);
1396
		}
1397
	}
1398
 
1399
	private void updateHiddenValue() {
1400
		if (hiddenInput != null) {
1401
			hiddenInput.setValue(collecterIdentifiants());
1402
		}
1403
	}
1404
 
1405
	private void mettreAJour(String origine) {
1406
		setSelection(getSelection());
1407
		setRawValue(collecterTexte());
1408
		updateHiddenValue();
1409
	}
1410
 
1411
	private void nettoyerListeDeCasesACocher() {
1412
		if (listView.isRendered()) {
1413
			List<D> listeADecocher = listView.getChecked();
1414
			for (D d : listeADecocher) {
1415
				listView.setChecked(d, false);
1416
			}
1417
		}
1418
	}
1419
 
1420
	public void peupler(String identifiants) {
1421
		peuplerAvecIdentifiant(identifiants);
1422
	}
1423
 
1424
	public void peuplerAvecIdentifiant(String identifiants) {
1425
		List<D> liste = parserChaine(identifiants, valueFieldSeparator, getValueField());
1426
		executerPeuplement(liste);
1427
	}
1428
 
1429
	public void peuplerAvecTexte(String texte) {
1430
		List<D> liste = parserChaine(texte, rawSeparator, getDisplayField());
1431
		executerPeuplement(liste);
1432
	}
1433
 
1434
	private void executerPeuplement(List<D> liste) {
1435
		listeInitiale = liste;
1436
		initialisation = false;
1437
		mettreAJour("executerPeuplement");
1438
	}
1439
 
1440
	public String collecter() {
1441
		return collecterIdentifiants();
1442
	}
1443
 
1444
	public String collecterIdentifiants() {
1445
		return executerCollecte(getSelection(), getValueField(), valueFieldSeparator);
1446
	}
1447
 
1448
	public String collecterTexte() {
1449
		return executerCollecte(getSelection(), getDisplayField(), rawSeparator);
1450
	}
1451
 
1452
	private String executerCollecte(List<D> selection, String champCle, String separateur) {
1453
		String chaineDeSortie = "";
1454
		Iterator<D> it = selection.iterator();
1455
		while (it.hasNext()) {
1456
			D d = it.next();
1457
			chaineDeSortie += d.get(champCle);
1458
			if (it.hasNext()) {
1459
				chaineDeSortie += separateur;
1460
			}
1461
		}
1462
		return chaineDeSortie;
1463
	}
1464
 
1465
	private List<D> parserChaine(String chaineAAnalyser, String separateur, String champCle) {
1466
		ArrayList<D> liste = new ArrayList<D>();
1467
		chaineAAnalyser = chaineAAnalyser.trim();
1468
		if (!UtilString.isEmpty(chaineAAnalyser)) {
1469
			String[] valeurs = chaineAAnalyser.split(separateur);
1470
			int nbreValeurs = valeurs.length;
1471
			if (nbreValeurs > 0 && getStore() != null) {
1472
				for (int i = 0; i < nbreValeurs; i++)	{
1473
					String valeur = valeurs[i];
1474
					D d	= getStore().findModel(champCle, valeur);
1475
					liste.add(d);
1476
				}
1477
			}
1478
		}
1479
		return liste;
1480
	}
1481
 
1482
	public String formaterTexteEnIdentifiants(String texte) {
1483
		List<D> liste = parserChaine(texte, rawSeparator, getDisplayField());
1484
		return executerCollecte(liste, getValueField(), valueFieldSeparator);
1485
	}
1486
 
1487
	public String formaterIdentifiantsEnTexte(String identifiants) {
1488
		List<D> liste = parserChaine(identifiants, valueFieldSeparator, getValueField());
1489
		return executerCollecte(liste, getDisplayField(), rawSeparator);
1490
	}
1491
}