1 /**
2 *
3 */
4 package org.votech.ds6.widgets;
5
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.net.URI;
9 import java.net.URL;
10 import java.util.Collection;
11 import java.util.List;
12 import java.util.Map;
13
14 import javax.swing.JFrame;
15 import javax.swing.JLabel;
16 import javax.swing.JMenu;
17 import javax.swing.JMenuBar;
18 import javax.swing.JMenuItem;
19 import javax.swing.SwingUtilities;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.votech.ds6.imageutils.ImageHelper;
24 import org.votech.ds6.imageutils.ResizableImageIcon;
25 import org.votech.plastic.CommonMessageConstants;
26 import org.votech.plastic.incoming.handlers.StandardHandler;
27 import org.votech.plastic.managers.MsgAction;
28 import org.votech.plastic.managers.PlasticApplication;
29 import org.votech.plastic.managers.PlasticConnectionManager;
30 import org.votech.plastic.managers.PlasticConnectionManagerImpl;
31 import org.votech.plastic.managers.PlasticRegistry;
32 import org.votech.plastic.managers.PlasticRegistryListener;
33 import org.votech.plastic.outgoing.messages.Message;
34 import org.votech.plastic.outgoing.messages.votable.ShowObjectsMessage;
35
36 /**
37 * A menu item capable of populating itself with entries based on available Plastic Applications, and sending
38 * appropriate messages when selected. This menu item is "message fragment" aware, that is to say, it can
39 * decode plastic messages of the form ivo://message#fragment and populate itself with icons and tooltips based
40 * on that fragment. As of 22/8/06 this isn't formally part of the plastic spec, but I hope it will be.
41 * This class needs the plastic library.
42 *
43 * @author jdt
44 *
45 */
46 public class JPlasticAwareMenu extends JMenu implements PlasticRegistryListener {
47 private ImageHelper imageHelper = new ImageHelper(this.getClass());
48
49 /**
50 * Logger for this class
51 */
52 private static final Log logger = LogFactory.getLog(JPlasticAwareMenu.class);
53
54 /**
55 *
56 */
57 private static final long serialVersionUID = 1267325085020507642L;
58
59 private URI messageUri;
60
61 private MessageSource messageSource;
62
63 private boolean asynch;
64
65 private PlasticRegistry registry;
66
67 /**
68 * Ctor
69 * @param string basic text entry on the UI
70 * @param registry a Plastic Registry
71 * @param messageUri the URI of the message this menu item will send
72 * @param messageSource a source of populated messages, that can accept returned results
73 * @param true if you want the messages sent asynch, without waiting for a result
74 */
75 public JPlasticAwareMenu(String string, PlasticRegistry registry, URI messageUri, MessageSource messageSource, boolean asynch) {
76 super(string);
77 this.messageSource = messageSource;
78 this.messageUri = messageUri;
79 this.asynch = asynch;
80 this.registry = registry;
81 registry.addListener(this);
82 setSubmenus();
83 }
84
85 private boolean userEnabled = true;
86 @SuppressWarnings("unchecked")
87 public void setEnabled(boolean b) {
88 userEnabled = b;
89
90 final Collection<PlasticApplication> relevantPlasticApplications = registry.getPlasticApplications(messageUri);
91 if (relevantPlasticApplications.isEmpty()) {
92 super.setEnabled(false);
93 return;
94 }
95 super.setEnabled(userEnabled);
96 }
97 public boolean isEnabled() {
98 return userEnabled;
99 }
100
101 /**
102 * Should only be called in the event thread.
103 *
104 */
105 @SuppressWarnings("unchecked")
106 private void setSubmenus() {
107 logger.info("Applications changed - updating submenus of "+this.getText());
108 logger.info("Registry holds "+registry.getPlasticApplications().size());
109 final Collection<PlasticApplication> relevantPlasticApplications = registry.getPlasticApplications(messageUri);
110 logger.info("of which "+relevantPlasticApplications.size()+" understands "+messageUri);
111 removeAll();
112 setEnabled(isEnabled());
113 if (relevantPlasticApplications.isEmpty()) {
114 setToolTipText("No suitable Plastic apps are currently connected");
115 return;
116 }
117 setToolTipText("");
118
119
120 JMenuItem allItem = new JMenuItem("All");
121 ResizableImageIcon worldicon = imageHelper.getIcon("world.jpg");
122 worldicon.resize(-1,20);
123 allItem.setIcon(worldicon);
124 allItem.addActionListener(new ActionListener() {
125
126 public void actionPerformed(ActionEvent e) {
127 logger.info("broadcasting message "+messageUri);
128
129 Message message = messageSource.getMessage(null);
130
131 if (asynch) {
132 registry.broadcastAsynch(message);
133 } else {
134 Map result = registry.broadcast(message);
135 messageSource.processReturnValues(result);
136 }
137 }
138
139 });
140 add(allItem);
141 addSeparator();
142
143 for (final PlasticApplication app : relevantPlasticApplications) {
144
145 final String description = app.getDescription();
146 final String name= app.getName();
147 final URL logoUrl = app.getIconUrl();
148 List<MsgAction> actions = app.getActions(messageUri);
149 logger.info("Adding "+name);
150 logger.info(name+" has "+actions.size()+" actions.");
151
152 JMenuItem appItem;
153 if (actions.size()==0) {
154
155 appItem = new JMenuItem(name);
156 appItem.addActionListener(new ActionListener() {
157
158 public void actionPerformed(ActionEvent e) {
159 logger.info("Sending message "+messageUri+" to "+name);
160
161 Message message = messageSource.getMessage(app);
162
163 if (asynch) {
164 app.sendMessageAsynch(message);
165 } else {
166 Object result = app.sendMessage(message);
167 messageSource.processReturnValue(app, result);
168 }
169 }
170
171 });
172 } else {
173 JMenu appItemMenu = new JMenu(name);
174 for (final MsgAction action : actions) {
175
176 final String actionName = action.getName();
177 final String actionDescription = action.getDescription();
178 final URL actionLogoUrl = action.getIconUrl();
179
180 logger.info("Adding action "+actionName);
181
182 JMenuItem actionMenuItem = new JMenuItem(actionName);
183 if (actionDescription!=null) actionMenuItem.setToolTipText(actionDescription);
184 if (actionLogoUrl!=null) {
185 ResizableImageIcon icon = new ResizableImageIcon(actionLogoUrl);
186 icon.resize(-1,20);
187 actionMenuItem.setIcon(icon);
188 }
189 appItemMenu.add(actionMenuItem);
190
191 actionMenuItem.addActionListener(new ActionListener() {
192
193 public void actionPerformed(ActionEvent e) {
194 logger.info("Sending message "+messageUri+" with action "+actionName+" to "+name);
195 Message message = messageSource.getMessage(app);
196 message.setAction(action.getRawFragment());
197 if (asynch) {
198 app.sendMessageAsynch(message);
199 } else {
200 Object result = app.sendMessage(message);
201 messageSource.processReturnValue(app, result);
202 }
203 }
204
205 });
206 }
207 appItem = appItemMenu;
208 }
209
210
211
212
213
214
215 if (logoUrl!=null) {
216 ResizableImageIcon icon = new ResizableImageIcon(logoUrl);
217 icon.resize(-1,20);
218 appItem.setIcon(icon);
219 }
220 if (description!=null) appItem.setToolTipText(description);
221
222 add(appItem);
223
224
225
226
227
228 }
229 }
230
231 /**
232 * A test harness
233 * @param args
234 */
235 public static void main(String[] args) {
236 PlasticConnectionManager manager = new PlasticConnectionManagerImpl("Menu Test",new StandardHandler("Menu Test",null,null,null),true,30000);
237 PlasticRegistry reg = new PlasticRegistry(manager);
238 JFrame frame = new JFrame("Menu test");
239 final JMenuBar menuBar = new JMenuBar();
240 final JMenu menu = new JMenu("Plastic");
241 menuBar.add(menu);
242 frame.setJMenuBar(menuBar);
243
244
245
246 MessageSource ms= new AbstractMessageSource() {
247
248 public Message getMessage(PlasticApplication destination) {
249 System.out.println("Constructing message for destination "+destination);
250 Message m = new ShowObjectsMessage("dummy", new int[] {});
251 return m;
252 }
253
254 public void processReturnValue(PlasticApplication sender, Object result) {
255 Boolean result1 = ShowObjectsMessage.convertReturn(result);
256 System.out.println("result from show objects from "+sender+" was "+result1);
257
258 }
259 };
260 menu.add(new JPlasticAwareMenu("send votable", reg, CommonMessageConstants.VO_SHOW_OBJECTS, ms, false));
261
262 frame.add(new JLabel("Test application"));
263 frame.pack();
264 frame.setVisible(true);
265 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
266 }
267
268 /**
269 * Notify me that the plastic-registered applications have changed.
270 * Only call me outwith the event thread.
271 */
272 public void applicationsChanged() {
273 SwingUtilities.invokeLater(new Runnable() {
274
275 public void run() {
276 setSubmenus();
277
278 }
279
280 });
281
282 }
283
284 public String toString() {
285 return "Plastic aware menu " + getText();
286 }
287
288 }