View Javadoc
1   package org.vaadin.tokenfield;
2   
3   import java.util.HashSet;
4   import java.util.Iterator;
5   import java.util.Random;
6   import java.util.Set;
7   
8   import org.vaadin.tokenfield.TokenField.InsertPosition;
9   
10  import com.vaadin.v7.data.Container;
11  import com.vaadin.v7.data.Property.ValueChangeEvent;
12  import com.vaadin.v7.data.Property.ValueChangeListener;
13  import com.vaadin.v7.data.util.BeanItem;
14  import com.vaadin.v7.data.util.BeanItemContainer;
15  import com.vaadin.server.VaadinRequest;
16  import com.vaadin.v7.shared.ui.combobox.FilteringMode;
17  import com.vaadin.ui.Alignment;
18  import com.vaadin.ui.Button;
19  import com.vaadin.ui.Button.ClickEvent;
20  import com.vaadin.v7.ui.CheckBox;
21  import com.vaadin.v7.ui.ComboBox;
22  import com.vaadin.ui.CssLayout;
23  import com.vaadin.v7.ui.Form;
24  import com.vaadin.ui.GridLayout;
25  import com.vaadin.v7.ui.HorizontalLayout;
26  import com.vaadin.ui.Layout;
27  import com.vaadin.v7.ui.ListSelect;
28  import com.vaadin.v7.ui.NativeSelect;
29  import com.vaadin.ui.Notification;
30  import com.vaadin.ui.Notification.Type;
31  import com.vaadin.ui.Panel;
32  import com.vaadin.ui.UI;
33  import com.vaadin.v7.ui.VerticalLayout;
34  import com.vaadin.ui.Window;
35  
36  public class DemoRoot extends UI {
37  
38      @Override
39      protected void init(VaadinRequest request) {
40  
41          setContent(new Content());
42      }
43  
44      static class Content extends VerticalLayout {
45  
46          Content() {
47              // Just add some spacing so it looks nicer
48              setSpacing(true);
49              setMargin(true);
50  
51              {
52                  /*
53                   * This is the most basic use case using all defaults; it's
54                   * empty to begin with, the user can enter new tokens.
55                   */
56  
57                  Panel p = new Panel("Basic");
58                  VerticalLayout l = new VerticalLayout();
59                  l.setMargin(true);
60                  p.setContent(l);
61                  addComponent(p);
62  
63                  TokenFieldld.html#TokenField">TokenField f = new TokenField("Add tags");
64                  l.addComponent(f);
65  
66              }
67  
68              {
69                  /*
70                   * Interpretes "," as token separator
71                   */
72  
73                  Panel p = new Panel("Comma separated");
74                  VerticalLayout l = new VerticalLayout();
75                  l.setMargin(true);
76                  p.setContent(l);
77                  addComponent(p);
78  
79                  TokenFieldld.html#TokenField">TokenField f = new TokenField() {
80  
81                      @Override
82                      protected void onTokenInput(Object tokenId) {
83                          String[] tokens = ((String) tokenId).split(",");
84                          for (String token : tokens) {
85                              token = token.trim();
86                              if (token.length() > 0) {
87                                  super.onTokenInput(token);
88                              }
89                          }
90                      }
91  
92                      @Override
93                      protected void rememberToken(String tokenId) {
94                          String[] tokens = ((String) tokenId).split(",");
95                          for (String token : tokens) {
96                              token = token.trim();
97                              if (token.length() > 0) {
98                                  super.rememberToken(token);
99                              }
100                         }
101                     }
102 
103                 };
104                 f.setInputPrompt("tag, another, yetanother");
105                 l.addComponent(f);
106 
107             }
108 
109             {
110                 /*
111                  * In this example, most features are exercised. A container
112                  * with generated contacts is used. The input has filtering
113                  * (a.k.a suggestions) enabled, and the added token button is
114                  * configured so that it is in the standard "Name <email>"
115                  * -format. New contacts can be added to the container ('address
116                  * book'), or added as-is (in which case it's styled
117                  * differently).
118                  */
119 
120                 Panel p = new Panel("Full featured example");
121                 VerticalLayout l = new VerticalLayout();
122                 l.setMargin(true);
123                 p.setContent(l);
124                 l.setStyleName("black");
125                 addComponent(p);
126 
127                 // generate container
128                 Container tokens = generateTestContainer();
129 
130                 // we want this to be vertical
131                 VerticalLayout lo = new VerticalLayout();
132                 lo.setSpacing(true);
133 
134                 final TokenFieldld.html#TokenField">TokenField f = new TokenField(lo) {
135 
136                     private static final long serialVersionUID = 5530375996928514871L;
137 
138                     // dialog if not in 'address book', otherwise just add
139                     protected void onTokenInput(Object tokenId) {
140                         Set<Object> set = (Set<Object>) getValue();
141                         Contact c = new Contact("", tokenId.toString());
142                         if (set != null && set.contains(c)) {
143                             // duplicate
144                             Notification.show(getTokenCaption(tokenId)
145                                     + " is already added");
146                         } else {
147                             if (!cb.containsId(c)) {
148                                 // don't add directly,
149                                 // show custom "add to address book" dialog
150                                 getUI().addWindow(
151                                         new EditContactWindow(tokenId
152                                                 .toString(), this));
153 
154                             } else {
155                                 // it's in the 'address book', just add
156                                 addToken(tokenId);
157                             }
158                         }
159                     }
160 
161                     // show confirm dialog
162                     protected void onTokenClick(final Object tokenId) {
163                         getUI().addWindow(
164                                 new RemoveWindow((Contact) tokenId, this));
165                     }
166 
167                     // just delete, no confirm
168                     protected void onTokenDelete(Object tokenId) {
169                         this.removeToken(tokenId);
170                     }
171 
172                     // custom caption + style if not in 'address book'
173                     protected void configureTokenButton(Object tokenId,
174                             Button button) {
175                         super.configureTokenButton(tokenId, button);
176                         // custom caption
177                         button.setCaption(getTokenCaption(tokenId) + " <"
178                                 + tokenId + ">");
179                         // width
180                         button.setWidth("100%");
181 
182                         if (!cb.containsId(tokenId)) {
183                             // it's not in the address book; style
184                             button.addStyleName(TokenField.STYLE_BUTTON_EMPHAZISED);
185                         }
186                     }
187                 };
188                 l.addComponent(f);
189                 // This would turn on the "fake tekstfield" look:
190                 f.setStyleName(TokenField.STYLE_TOKENFIELD);
191                 f.setWidth("100%");
192                 f.setInputWidth("100%");
193                 f.setContainerDataSource(tokens); // 'address book'
194                 f.setFilteringMode(FilteringMode.CONTAINS); // suggest
195                 f.setTokenCaptionPropertyId("name"); // use name in input
196                 f.setInputPrompt("Enter contact name or new email address");
197                 f.setRememberNewTokens(false); // we'll do this via the dialog
198                 // Pre-add a few:
199                 Iterator it = f.getTokenIds().iterator();
200                 f.addToken(it.next());
201                 f.addToken(it.next());
202                 f.addToken(new Contact("", "thatnewguy@example.com"));
203 
204             }
205 
206             {
207                 /*
208                  * This example uses to selects to dynamically change the insert
209                  * position and the layout used.
210                  */
211 
212                 final Panel p = new Panel("Layout and InsertPosition");
213                 final VerticalLayout l = new VerticalLayout();
214                 l.setMargin(true);
215                 p.setContent(l);
216                 l.setSpacing(true);
217                 addComponent(p);
218 
219                 HorizontalLayout controls = new HorizontalLayout();
220                 l.addComponent(controls);
221 
222                 // generate container
223                 Container tokens = generateTestContainer();
224 
225                 // w/ datasource, no configurator
226                 final TokenFieldld.html#TokenField">TokenField f = new TokenField();
227                 /*
228                  * f.setContainerDataSource(tokens); //
229                  * f.setNewTokensAllowed(false);
230                  * f.setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS);
231                  * f.setInputPrompt("firstname.lastname@example.com"); -
232                  */
233                 l.addComponent(f);
234 
235                 final NativeSelect lo = new NativeSelect("Layout");
236                 lo.setImmediate(true);
237                 lo.addItem(HorizontalLayout.class);
238                 lo.addItem(VerticalLayout.class);
239                 lo.addItem(GridLayout.class);
240                 lo.addItem(CssLayout.class);
241                 lo.setNullSelectionAllowed(false);
242                 lo.setValue(f.getLayout().getClass());
243                 lo.addValueChangeListener(new ValueChangeListener() {
244                     private static final long serialVersionUID = -5644191531547324609L;
245 
246                     private TokenField curr = f;
247 
248                     @Override
249                     public void valueChange(ValueChangeEvent event) {
250                         try {
251                             Layout l = (Layout) ((Class) event.getProperty()
252                                     .getValue()).newInstance();
253                             if (l instanceof GridLayout) {
254                                 ((GridLayout) l).setColumns(3);
255                             }
256                             l.removeComponent(curr);
257                             curr = new TokenField(l);
258                             l.addComponent(curr);
259                         } catch (Exception e) {
260                             Notification.show("Ouch!",
261                                     "Could not make a " + lo.getValue(),
262                                     Type.ERROR_MESSAGE);
263                             lo.setValue(f.getLayout().getClass());
264                             e.printStackTrace();
265                         }
266                     }
267                 });
268 
269                 controls.addComponent(lo);
270 
271                 final NativeSelect ip = new NativeSelect("InsertPosition");
272                 ip.setImmediate(true);
273                 ip.addItem(InsertPosition.AFTER);
274                 ip.addItem(InsertPosition.BEFORE);
275                 ip.setNullSelectionAllowed(false);
276                 ip.setValue(f.getTokenInsertPosition());
277                 ip.addValueChangeListener(new ValueChangeListener() {
278 
279                     private static final long serialVersionUID = 518234140117517538L;
280 
281                     public void valueChange(ValueChangeEvent event) {
282                         f.setTokenInsertPosition((InsertPosition) ip.getValue());
283                     }
284                 });
285                 controls.addComponent(ip);
286 
287                 final CheckBox cb = new CheckBox("Read-only");
288                 cb.setImmediate(true);
289                 cb.setValue(f.isReadOnly());
290                 cb.addValueChangeListener(new ValueChangeListener() {
291 
292                     private static final long serialVersionUID = 8812909594903040042L;
293 
294                     public void valueChange(ValueChangeEvent event) {
295                         f.setReadOnly(cb.getValue());
296                     }
297                 });
298                 controls.addComponent(cb);
299                 controls.setComponentAlignment(cb, Alignment.BOTTOM_LEFT);
300 
301             }
302 
303             {
304                 Panel p = new Panel("Data binding and buffering");
305                 addComponent(p);
306 
307                 // just for layout; ListSelect left, TokenField right
308                 HorizontalLayout lo = new HorizontalLayout();
309                 lo.setWidth("100%");
310                 lo.setSpacing(true);
311                 lo.setMargin(true);
312                 p.setContent(lo);
313 
314                 // A regular list select
315                 ListSelect list = new ListSelect(
316                         "ListSelect, datasource for TokenField");
317                 list.setWidth("220px");
318                 lo.addComponent(list);
319                 list.setImmediate(true);
320                 list.setMultiSelect(true);
321                 // Add a few items
322                 list.addItem("One");
323                 list.addItem("Two");
324                 list.addItem("Three");
325                 list.addItem("Four");
326                 list.addItem("Five");
327 
328                 // TokenField bound to the ListSelect above, CssLayout so that
329                 // it wraps nicely.
330                 final TokenFieldld.html#TokenField">TokenField f = new TokenField(
331                         "TokenField, buffered, click << to commit");
332                 f.setContainerDataSource(list.getContainerDataSource());
333                 f.setBuffered(true);
334                 // f.setNewTokensAllowed(false);
335                 f.setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS);
336                 f.setPropertyDataSource(list);
337 
338                 lo.addComponent(new Button("<<", new Button.ClickListener() {
339 
340                     private static final long serialVersionUID = 1375470313147460732L;
341 
342                     public void buttonClick(ClickEvent event) {
343                         f.commit();
344                     }
345                 }));
346 
347                 lo.addComponent(f);
348                 lo.setExpandRatio(f, 1.0f);
349 
350             }
351         }
352     }
353 
354     /**
355      * This is the window used to add new contacts to the 'address book'. It
356      * does not do proper validation - you can add weird stuff.
357      */
358     public static class EditContactWindow extends Window {
359         private Contact contact;
360 
361         EditContactWindow(final String t, final TokenField f) {
362             super("New Contact");
363             VerticalLayout l = new VerticalLayout();
364             setContent(l);
365             if (t.contains("@")) {
366                 contact = new Contact("", t);
367             } else {
368                 contact = new Contact(t, "");
369             }
370             setModal(true);
371             center();
372             setWidth("250px");
373             setStyleName("black");
374             setResizable(false);
375 
376             // Just bind a Form to the Contact -pojo via BeanItem
377             Form form = new Form();
378             form.setItemDataSource(new BeanItem<Contact>(contact));
379             form.setImmediate(true);
380             l.addComponent(form);
381 
382             // layout buttons horizontally
383             HorizontalLayout hz = new HorizontalLayout();
384             l.addComponent(hz);
385             hz.setSpacing(true);
386             hz.setWidth("100%");
387 
388             Button dont = new Button("Don't add", new Button.ClickListener() {
389 
390                 private static final long serialVersionUID = -1198191849568844582L;
391 
392                 public void buttonClick(ClickEvent event) {
393                     if (contact.getEmail() == null
394                             || contact.getEmail().length() < 1) {
395                         contact.setEmail(contact.getName());
396                     }
397                     f.addToken(contact);
398                     f.getUI().removeWindow(EditContactWindow.this);
399                 }
400             });
401             hz.addComponent(dont);
402             hz.setComponentAlignment(dont, Alignment.MIDDLE_LEFT);
403 
404             Button add = new Button("Add to contacts",
405                     new Button.ClickListener() {
406 
407                         private static final long serialVersionUID = 1L;
408 
409                         public void buttonClick(ClickEvent event) {
410                             if (contact.getEmail() == null
411                                     || contact.getEmail().length() < 1) {
412                                 contact.setEmail(contact.getName());
413                             }
414                             ((BeanItemContainer) f.getContainerDataSource())
415                                     .addBean(contact);
416                             f.addToken(contact);
417                             f.getUI().removeWindow(EditContactWindow.this);
418                         }
419                     });
420             hz.addComponent(add);
421             hz.setComponentAlignment(add, Alignment.MIDDLE_RIGHT);
422 
423         }
424     }
425 
426     /* Used to generate example contents */
427     private static final String[] firstnames = new String[] { "John", "Mary",
428             "Joe", "Sarah", "Jeff", "Jane", "Peter", "Marc", "Robert", "Paula",
429             "Lenny", "Kenny", "Nathan", "Nicole", "Laura", "Jos", "Josie",
430             "Linus" };
431     private static final String[] lastnames = new String[] { "Torvalds",
432             "Smith", "Adams", "Black", "Wilson", "Richards", "Thompson",
433             "McGoff", "Halas", "Jones", "Beck", "Sheridan", "Picard", "Hill",
434             "Fielding", "Einstein" };
435 
436     private static Container generateTestContainer() {
437         BeanItemContainer<Contact> container = new BeanItemContainer<Contact>(
438                 Contact.class);
439 
440         HashSet<String> log = new HashSet<String>();
441         Random r = new Random(5);
442         for (int i = 0; i < 20;) {
443             String fn = firstnames[(int) (r.nextDouble() * firstnames.length)];
444             String ln = lastnames[(int) (r.nextDouble() * lastnames.length)];
445             String name = fn + " " + ln;
446             String email = fn.toLowerCase() + "." + ln.toLowerCase()
447                     + "@example.com";
448 
449             if (!log.contains(email)) {
450                 log.add(email);
451                 container.addBean(new Contact(name, email));
452                 i++;
453             }
454 
455         }
456         return container;
457     }
458 
459     /**
460      * Example Contact -bean, mostly generated setters/getters.
461      */
462     public static class Contact {
463         private String name;
464         private String email;
465 
466         public Contact(String name, String email) {
467             this.name = name;
468             this.email = email;
469         }
470 
471         public String getName() {
472             return name;
473         }
474 
475         public void setName(String name) {
476             this.name = name;
477         }
478 
479         public String getEmail() {
480             return email;
481         }
482 
483         public void setEmail(String email) {
484             this.email = email;
485         }
486 
487         public String toString() {
488             return email;
489         }
490 
491         @Override
492         public boolean equals(Object obj) {
493             if (obj instanceof Contact) {
494                 return email.equals(((Contact) obj).getEmail());
495             }
496             return false;
497         }
498 
499         @Override
500         public int hashCode() {
501             return email.hashCode();
502         }
503 
504     }
505 
506     /**
507      * This is the window used to confirm removal
508      */
509     public static class RemoveWindow extends Window {
510 
511         private static final long serialVersionUID = -7140907025722511460L;
512 
513         RemoveWindow(final Contact c, final TokenField f) {
514             super("Remove " + c.getName() + "?");
515 
516             VerticalLayout l = new VerticalLayout();
517             setContent(l);
518 
519             setStyleName("black");
520             setResizable(false);
521             center();
522             setModal(true);
523             setWidth("250px");
524             setClosable(false);
525 
526             // layout buttons horizontally
527             HorizontalLayout hz = new HorizontalLayout();
528             l.addComponent(hz);
529             hz.setSpacing(true);
530             hz.setWidth("100%");
531 
532             Button cancel = new Button("Cancel", new Button.ClickListener() {
533 
534                 private static final long serialVersionUID = 7675170261217815011L;
535 
536                 public void buttonClick(ClickEvent event) {
537                     f.getUI().removeWindow(RemoveWindow.this);
538                 }
539             });
540             hz.addComponent(cancel);
541             hz.setComponentAlignment(cancel, Alignment.MIDDLE_LEFT);
542 
543             Button remove = new Button("Remove", new Button.ClickListener() {
544 
545                 private static final long serialVersionUID = 5004855711589989635L;
546 
547                 public void buttonClick(ClickEvent event) {
548                     f.removeToken(c);
549                     f.getUI().removeWindow(RemoveWindow.this);
550                 }
551             });
552             hz.addComponent(remove);
553             hz.setComponentAlignment(remove, Alignment.MIDDLE_RIGHT);
554 
555         }
556     }
557 
558 }