1 package org.vaadin.peter.contextmenu;
2
3 import java.lang.reflect.Method;
4 import java.util.ArrayList;
5 import java.util.EventListener;
6 import java.util.EventObject;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.UUID;
11
12 import org.vaadin.peter.contextmenu.client.ContextMenuClientRpc;
13 import org.vaadin.peter.contextmenu.client.ContextMenuServerRpc;
14 import org.vaadin.peter.contextmenu.client.ContextMenuState;
15 import org.vaadin.peter.contextmenu.client.ContextMenuState.ContextMenuItemState;
16
17 import com.vaadin.event.ItemClickEvent;
18 import com.vaadin.event.ItemClickEvent.ItemClickListener;
19 import com.vaadin.server.AbstractClientConnector;
20 import com.vaadin.server.AbstractExtension;
21 import com.vaadin.server.Resource;
22 import com.vaadin.shared.MouseEventDetails.MouseButton;
23 import com.vaadin.ui.Component;
24 import com.vaadin.ui.Table;
25 import com.vaadin.ui.Table.FooterClickEvent;
26 import com.vaadin.ui.Table.FooterClickListener;
27 import com.vaadin.ui.Table.HeaderClickEvent;
28 import com.vaadin.ui.Table.HeaderClickListener;
29 import com.vaadin.ui.Tree;
30 import com.vaadin.ui.UI;
31 import com.vaadin.util.ReflectTools;
32
33 public class ContextMenu extends AbstractExtension {
34 private static final long serialVersionUID = 4275181115413786498L;
35
36 private Map<String, ContextMenuItem> items;
37
38 private ContextMenuServerRpc serverRPC = new ContextMenuServerRpc() {
39 private static final long serialVersionUID = 5622864428554337992L;
40
41 @Override
42 public void itemClicked(String itemId, boolean menuClosed) {
43 ContextMenuItem item = items.get(itemId);
44
45 item.notifyClickListeners();
46 fireEvent(new ContextMenuItemClickEvent(item));
47 }
48
49 @Override
50 public void onContextMenuOpenRequested(int x, int y, String connectorId) {
51 fireEvent(new ContextMenuOpenedOnComponentEvent(ContextMenu.this,
52 x, y, (Component) UI.getCurrent().getConnectorTracker()
53 .getConnector(connectorId)));
54 }
55
56 @Override
57 public void contextMenuClosed() {
58 fireEvent(new ContextMenuClosedEvent(ContextMenu.this));
59 }
60 };
61
62 public ContextMenu() {
63 registerRpc(serverRPC);
64
65 items = new HashMap<String, ContextMenu.ContextMenuItem>();
66
67 setOpenAutomatically(true);
68 }
69
70 protected String getNextId() {
71 return UUID.randomUUID().toString();
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 public void setOpenAutomatically(boolean openAutomatically) {
88 getState().setOpenAutomatically(openAutomatically);
89 }
90
91
92
93
94
95
96
97 public ContextMenuItem addItem(String caption) {
98 ContextMenuItemState itemState = getState().addChild(caption,
99 getNextId());
100
101 ContextMenuItem item = new ContextMenuItem(itemState);
102 items.put(itemState.id, item);
103
104 return item;
105 }
106
107
108
109
110
111
112
113 public ContextMenuItem addItem(Resource icon) {
114 ContextMenuItem item = addItem("");
115 item.setIcon(icon);
116 return item;
117 }
118
119
120
121
122
123
124
125
126 public ContextMenuItem addItem(String caption, Resource icon) {
127 ContextMenuItem item = addItem(caption);
128 item.setIcon(icon);
129 return item;
130 }
131
132
133
134
135
136
137 public void removeRootItem(ContextMenuItem contextMenuItem) {
138 items.remove(contextMenuItem);
139 getState().getRootItems().remove(contextMenuItem);
140 }
141
142
143
144
145 public void removeAllItems() {
146 items.clear();
147 getState().getRootItems().clear();
148 }
149
150
151
152
153
154
155 public void setAsTableContextMenu(final Table table) {
156 extend(table);
157
158 table.addItemClickListener(new ItemClickEvent.ItemClickListener() {
159 private static final long serialVersionUID = -348059189217149508L;
160
161 @Override
162 public void itemClick(ItemClickEvent event) {
163 if (event.getButton() == MouseButton.RIGHT) {
164 fireEvent(new ContextMenuOpenedOnTableRowEvent(
165 ContextMenu.this, table, event.getItemId(), event
166 .getPropertyId()));
167 open(event.getClientX(), event.getClientY());
168 }
169 }
170 });
171
172 table.addHeaderClickListener(new HeaderClickListener() {
173 private static final long serialVersionUID = -5880755689414670581L;
174
175 @Override
176 public void headerClick(HeaderClickEvent event) {
177 if (event.getButton() == MouseButton.RIGHT) {
178 fireEvent(new ContextMenuOpenedOnTableHeaderEvent(
179 ContextMenu.this, table, event.getPropertyId()));
180 open(event.getClientX(), event.getClientY());
181 }
182 }
183 });
184
185 table.addFooterClickListener(new FooterClickListener() {
186 private static final long serialVersionUID = 2884227013964132482L;
187
188 @Override
189 public void footerClick(FooterClickEvent event) {
190 if (event.getButton() == MouseButton.RIGHT) {
191 fireEvent(new ContextMenuOpenedOnTableHeaderEvent(
192 ContextMenu.this, table, event.getPropertyId()));
193 open(event.getClientX(), event.getClientY());
194 }
195 }
196 });
197 }
198
199
200
201
202
203
204 public void setAsTreeContextMenu(final Tree tree) {
205 extend(tree);
206
207 tree.addItemClickListener(new ItemClickListener() {
208 private static final long serialVersionUID = 338499886052623304L;
209
210 @Override
211 public void itemClick(ItemClickEvent event) {
212 if (event.getButton() == MouseButton.RIGHT) {
213 fireEvent(new ContextMenuOpenedOnTreeItemEvent(
214 ContextMenu.this, tree, event.getItemId()));
215 open(event.getClientX(), event.getClientY());
216 }
217 }
218 });
219 }
220
221
222
223
224
225
226
227 public void setAsContextMenuOf(AbstractClientConnector component) {
228 if (component instanceof Table) {
229 setAsTableContextMenu((Table) component);
230 } else if (component instanceof Tree) {
231 setAsTreeContextMenu((Tree) component);
232 } else {
233 super.extend(component);
234 }
235 }
236
237
238
239
240
241
242
243
244
245
246
247 public void open(int x, int y) {
248 getRpcProxy(ContextMenuClientRpc.class).showContextMenu(x, y);
249 }
250
251 @Override
252 protected ContextMenuState getState() {
253 return (ContextMenuState) super.getState();
254 }
255
256
257
258
259
260
261
262 public void addItemClickListener(
263 ContextMenu.ContextMenuItemClickListener clickListener) {
264 addListener(ContextMenuItemClickEvent.class, clickListener,
265 ContextMenuItemClickListener.ITEM_CLICK_METHOD);
266 }
267
268
269
270
271
272
273
274 public void addContextMenuTableListener(
275 ContextMenu.ContextMenuOpenedListener.TableListener contextMenuTableListener) {
276 addListener(
277 ContextMenuOpenedOnTableRowEvent.class,
278 contextMenuTableListener,
279 ContextMenuOpenedListener.TableListener.MENU_OPENED_FROM_TABLE_ROW_METHOD);
280 addListener(
281 ContextMenuOpenedOnTableHeaderEvent.class,
282 contextMenuTableListener,
283 ContextMenuOpenedListener.TableListener.MENU_OPENED_FROM_TABLE_HEADER_METHOD);
284 addListener(
285 ContextMenuOpenedOnTableFooterEvent.class,
286 contextMenuTableListener,
287 ContextMenuOpenedListener.TableListener.MENU_OPENED_FROM_TABLE_FOOTER_METHOD);
288 }
289
290
291
292
293
294
295
296 public void addContextMenuTreeListener(
297 ContextMenu.ContextMenuOpenedListener.TreeListener contextMenuTreeListener) {
298 addListener(
299 ContextMenuOpenedOnTreeItemEvent.class,
300 contextMenuTreeListener,
301 ContextMenuOpenedListener.TreeListener.MENU_OPENED_FROM_TREE_ITEM_METHOD);
302 }
303
304
305
306
307
308
309 public void addContextMenuCloseListener(
310 ContextMenuClosedListener contextMenuClosedListener) {
311 addListener(
312 ContextMenuClosedEvent.class,
313 contextMenuClosedListener,
314 ContextMenuClosedListener.MENU_CLOSED);
315 }
316
317
318
319
320
321
322
323 public void addContextMenuComponentListener(
324 ContextMenu.ContextMenuOpenedListener.ComponentListener contextMenuComponentListener) {
325 addListener(
326 ContextMenuOpenedOnComponentEvent.class,
327 contextMenuComponentListener,
328 ContextMenuOpenedListener.ComponentListener.MENU_OPENED_FROM_COMPONENT);
329 }
330
331
332
333
334
335
336
337
338 public class ContextMenuItem {
339 private final ContextMenuItemState state;
340
341 private List<ContextMenu.ContextMenuItemClickListener> clickListeners;
342
343 private Object data;
344
345 protected ContextMenuItem(ContextMenuItemState itemState) {
346 if (itemState == null) {
347 throw new NullPointerException(
348 "Context menu item state must not be null");
349 }
350
351 clickListeners = new ArrayList<ContextMenu.ContextMenuItemClickListener>();
352 this.state = itemState;
353 }
354
355 protected void notifyClickListeners() {
356 for (ContextMenu.ContextMenuItemClickListener clickListener : clickListeners) {
357 clickListener
358 .contextMenuItemClicked(new ContextMenuItemClickEvent(
359 this));
360 }
361 }
362
363
364
365
366
367
368
369 public void setData(Object data) {
370 this.data = data;
371 }
372
373
374
375
376 public Object getData() {
377 return data;
378 }
379
380
381
382
383
384
385
386 public ContextMenuItem addItem(String caption) {
387 ContextMenuItemState childItemState = state.addChild(caption,
388 getNextId());
389 ContextMenuItem item = new ContextMenuItem(childItemState);
390
391 items.put(childItemState.id, item);
392 markAsDirty();
393 return item;
394 }
395
396
397
398
399
400
401
402 public ContextMenuItem addItem(Resource icon) {
403 ContextMenuItem item = this.addItem("");
404 item.setIcon(icon);
405
406 return item;
407 }
408
409
410
411
412
413
414
415
416 public ContextMenuItem addItem(String caption, Resource icon) {
417 ContextMenuItem item = this.addItem(caption);
418 item.setIcon(icon);
419
420 return item;
421 }
422
423
424
425
426
427
428 public void setIcon(Resource icon) {
429 setResource(state.id, icon);
430 }
431
432
433
434
435 public Resource getIcon() {
436 return getResource(state.id);
437 }
438
439
440
441
442
443
444 public void setSeparatorVisible(boolean separatorVisible) {
445 state.separator = separatorVisible;
446 }
447
448
449
450
451
452 public boolean hasSeparator() {
453 return state.separator;
454 }
455
456
457
458
459
460
461 public void setEnabled(boolean enabled) {
462 state.enabled = enabled;
463 }
464
465
466
467
468 public boolean isEnabled() {
469 return state.enabled;
470 }
471
472
473
474
475 public boolean hasSubMenu() {
476 return state.getChildren().size() > 0;
477 }
478
479
480
481
482
483
484
485 public void addItemClickListener(
486 ContextMenu.ContextMenuItemClickListener clickListener) {
487 this.clickListeners.add(clickListener);
488 }
489
490
491
492
493
494
495
496 public void removeItemClickListener(
497 ContextMenu.ContextMenuItemClickListener clickListener) {
498 this.clickListeners.remove(clickListener);
499 }
500
501 @Override
502 public boolean equals(Object other) {
503 if (this == other) {
504 return true;
505 }
506
507 if (other instanceof ContextMenuItem) {
508 return state.id.equals(((ContextMenuItem) other).state.id);
509 }
510
511 return false;
512 }
513
514 @Override
515 public int hashCode() {
516 return state.id.hashCode();
517 }
518 }
519
520
521
522
523
524 public interface ContextMenuItemClickListener extends EventListener {
525
526 public static final Method ITEM_CLICK_METHOD = ReflectTools.findMethod(
527 ContextMenuItemClickListener.class, "contextMenuItemClicked",
528 ContextMenuItemClickEvent.class);
529
530
531
532
533
534
535
536 public void contextMenuItemClicked(ContextMenuItemClickEvent event);
537 }
538
539
540
541
542
543
544 public static class ContextMenuItemClickEvent extends EventObject {
545 private static final long serialVersionUID = -3301204853129409248L;
546
547 public ContextMenuItemClickEvent(Object component) {
548 super(component);
549 }
550 }
551
552
553
554
555
556 public interface ContextMenuClosedListener extends EventListener {
557 public static final Method MENU_CLOSED = ReflectTools
558 .findMethod(ContextMenuClosedListener.class,
559 "onContextMenuClosed",
560 ContextMenuClosedEvent.class);
561
562
563
564
565
566 public void onContextMenuClosed(ContextMenuClosedEvent event);
567 }
568
569
570
571
572
573 public static class ContextMenuClosedEvent extends EventObject {
574 private static final long serialVersionUID = -5705205542849351984L;
575
576 private ContextMenu contextMenu;
577
578 public ContextMenuClosedEvent(ContextMenu contextMenu) {
579 super(contextMenu);
580 this.contextMenu = contextMenu;
581 }
582
583 public ContextMenu getContextMenu() {
584 return contextMenu;
585 }
586 }
587
588
589
590
591
592
593
594
595
596
597 public interface ContextMenuOpenedListener extends EventListener {
598
599 public interface ComponentListener extends ContextMenuOpenedListener {
600
601 public static final Method MENU_OPENED_FROM_COMPONENT = ReflectTools
602 .findMethod(
603 ContextMenuOpenedListener.ComponentListener.class,
604 "onContextMenuOpenFromComponent",
605 ContextMenuOpenedOnComponentEvent.class);
606
607
608
609
610
611
612
613 public void onContextMenuOpenFromComponent(
614 ContextMenuOpenedOnComponentEvent event);
615 }
616
617
618
619
620
621
622
623 public interface TableListener extends ContextMenuOpenedListener {
624
625 public static final Method MENU_OPENED_FROM_TABLE_ROW_METHOD = ReflectTools
626 .findMethod(ContextMenuOpenedListener.TableListener.class,
627 "onContextMenuOpenFromRow",
628 ContextMenuOpenedOnTableRowEvent.class);
629
630 public static final Method MENU_OPENED_FROM_TABLE_HEADER_METHOD = ReflectTools
631 .findMethod(ContextMenuOpenedListener.TableListener.class,
632 "onContextMenuOpenFromHeader",
633 ContextMenuOpenedOnTableHeaderEvent.class);
634
635 public static final Method MENU_OPENED_FROM_TABLE_FOOTER_METHOD = ReflectTools
636 .findMethod(ContextMenuOpenedListener.TableListener.class,
637 "onContextMenuOpenFromFooter",
638 ContextMenuOpenedOnTableFooterEvent.class);
639
640
641
642
643
644
645
646 public void onContextMenuOpenFromRow(
647 ContextMenuOpenedOnTableRowEvent event);
648
649
650
651
652
653
654
655 public void onContextMenuOpenFromHeader(
656 ContextMenuOpenedOnTableHeaderEvent event);
657
658
659
660
661
662
663
664 public void onContextMenuOpenFromFooter(
665 ContextMenuOpenedOnTableFooterEvent event);
666 }
667
668 public interface TreeListener extends ContextMenuOpenedListener {
669 public static final Method MENU_OPENED_FROM_TREE_ITEM_METHOD = ReflectTools
670 .findMethod(ContextMenuOpenedListener.TreeListener.class,
671 "onContextMenuOpenFromTreeItem",
672 ContextMenuOpenedOnTreeItemEvent.class);
673
674
675
676
677
678
679
680 public void onContextMenuOpenFromTreeItem(
681 ContextMenuOpenedOnTreeItemEvent event);
682 }
683
684 }
685
686
687
688
689
690 public static class ContextMenuOpenedOnTreeItemEvent extends EventObject {
691 private static final long serialVersionUID = -7705205542849351984L;
692
693 private Object itemId;
694 private ContextMenu contextMenu;
695
696 public ContextMenuOpenedOnTreeItemEvent(ContextMenu contextMenu,
697 Tree tree, Object itemId) {
698 super(tree);
699
700 this.contextMenu = contextMenu;
701 this.itemId = itemId;
702 }
703
704 public ContextMenu getContextMenu() {
705 return contextMenu;
706 }
707
708 public Object getItemId() {
709 return itemId;
710 }
711 }
712
713
714
715
716
717 public static class ContextMenuOpenedOnTableHeaderEvent extends EventObject {
718 private static final long serialVersionUID = -1220618848356241248L;
719
720 private Object propertyId;
721
722 private ContextMenu contextMenu;
723
724 public ContextMenuOpenedOnTableHeaderEvent(ContextMenu contextMenu,
725 Table source, Object propertyId) {
726 super(source);
727
728 this.contextMenu = contextMenu;
729 this.propertyId = propertyId;
730 }
731
732 public ContextMenu getContextMenu() {
733 return contextMenu;
734 }
735
736 public Object getPropertyId() {
737 return propertyId;
738 }
739 }
740
741
742
743
744
745 public static class ContextMenuOpenedOnTableFooterEvent extends EventObject {
746 private static final long serialVersionUID = 1999781663913723438L;
747
748 private Object propertyId;
749
750 private ContextMenu contextMenu;
751
752 public ContextMenuOpenedOnTableFooterEvent(ContextMenu contextMenu,
753 Table source, Object propertyId) {
754 super(source);
755
756 this.contextMenu = contextMenu;
757 this.propertyId = propertyId;
758 }
759
760 public ContextMenu getContextMenu() {
761 return contextMenu;
762 }
763
764 public Object getPropertyId() {
765 return propertyId;
766 }
767 }
768
769
770
771
772
773 public static class ContextMenuOpenedOnTableRowEvent extends EventObject {
774 private static final long serialVersionUID = -470218301318358912L;
775
776 private ContextMenu contextMenu;
777 private Object propertyId;
778 private Object itemId;
779
780 public ContextMenuOpenedOnTableRowEvent(ContextMenu contextMenu,
781 Table table, Object itemId, Object propertyId) {
782 super(table);
783
784 this.contextMenu = contextMenu;
785 this.itemId = itemId;
786 this.propertyId = propertyId;
787 }
788
789 public ContextMenu getContextMenu() {
790 return contextMenu;
791 }
792
793 public Object getItemId() {
794 return itemId;
795 }
796
797 public Object getPropertyId() {
798 return propertyId;
799 }
800 }
801
802
803
804
805
806
807 public static class ContextMenuOpenedOnComponentEvent extends EventObject {
808 private static final long serialVersionUID = 947108059398706966L;
809
810 private final ContextMenu contextMenu;
811
812 private final int x;
813 private final int y;
814
815 public ContextMenuOpenedOnComponentEvent(ContextMenu contextMenu,
816 int x, int y, Component component) {
817 super(component);
818
819 this.contextMenu = contextMenu;
820 this.x = x;
821 this.y = y;
822 }
823
824
825
826
827 public ContextMenu getContextMenu() {
828 return contextMenu;
829 }
830
831
832
833
834 public int getX() {
835 return x;
836 }
837
838
839
840
841 public int getY() {
842 return y;
843 }
844 }
845 }