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