View Javadoc

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         //only enable if we have some apps
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(); //start with a clean sheet
136         setEnabled(isEnabled()); //update enabled status
137         if (relevantPlasticApplications.isEmpty()) {
138             setToolTipText("No suitable Plastic apps are currently connected");
139             return;
140         }
141         setToolTipText("");
142         
143         //Broadcast to all
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); //TODO demote to debug
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                 //only have the default action
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); //TODO demote to debug
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); //TODO demote to debug
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 }