View Javadoc
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          //only enable if we have some apps
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(); //start with a clean sheet
112         setEnabled(isEnabled()); //update enabled status
113         if (relevantPlasticApplications.isEmpty()) {
114             setToolTipText("No suitable Plastic apps are currently connected");
115             return;
116         }
117         setToolTipText("");
118         
119         //Broadcast to all
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); //TODO demote to debug
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                 //only have the default action
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); //TODO demote to debug
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); //TODO demote to debug
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 }