1 package siouxsie.desktop.commands.impl;
2
3 import java.net.MalformedURLException;
4 import java.net.URL;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.LinkedHashMap;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13
14 import javax.swing.Icon;
15 import javax.swing.ImageIcon;
16 import javax.swing.JMenuBar;
17 import javax.swing.JToolBar;
18 import javax.swing.KeyStroke;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.pietschy.command.ActionCommand;
23 import org.pietschy.command.Command;
24 import org.pietschy.command.CommandGroup;
25 import org.pietschy.command.CommandManager;
26
27 import siouxsie.desktop.commands.CommandDescription;
28 import siouxsie.desktop.commands.Face;
29 import siouxsie.desktop.commands.Group;
30 import siouxsie.desktop.commands.ICommand;
31 import siouxsie.desktop.commands.ICommandInitializer;
32 import siouxsie.desktop.commands.IExceptionHandler;
33 import siouxsie.desktop.commands.IToggleCommand;
34
35 /**
36 * Commands handler. Creates menu bar and tool bar for
37 * applications.
38 * @author Arnaud Cogoluegnes
39 * @version $Id$
40 * @todo reimplement this basic implementation
41 * @todo handle exclusive toggle command (not very important)
42 * @todo handle sub menu (done, not good without ordering and presentation)
43 */
44 public class CommandsHandler {
45
46 /** name for default menu and tool bar */
47 private static final String DEFAULT_APP = "_siouxsie_default_app";
48
49 /** faces configuration */
50 private List<Face> faces;
51 /** groups configuration */
52 private List<Group> groups;
53 /** commands configuration */
54 private List<CommandDescription> commandDescriptions;
55
56 /** gui command manager listener */
57 private List<CommandManagerListenerContribution> commandManagerListeners = new ArrayList<CommandManagerListenerContribution>();
58
59 private Map<String, ActionCommand> idToCommand = new LinkedHashMap<String, ActionCommand>();
60
61 /** the apps (from the app command) */
62 private Set<String> apps = new HashSet<String>();
63
64 private IExceptionHandler exceptionHandler;
65
66 private CommandManager commandManager;
67
68 private Map<String, JMenuBar> appsMenuBars = new HashMap<String, JMenuBar>();
69
70 private Map<String, JToolBar> appsToolBars = new HashMap<String, JToolBar>();
71
72 private Map<String, ICommand> siouxsieCommands = new HashMap<String, ICommand>();
73
74 private static final Log LOG = LogFactory.getLog(CommandsHandler.class);
75
76 private ICommandInitializer commandInitializerService;
77
78 private static final String FACE_SEP_BEFORE_KEY = CommandsHandler.class.getName()+"_face_sep_before";
79 private static final String FACE_SEP_AFTER_KEY = CommandsHandler.class.getName()+"_face_sep_after";
80
81 public ICommandInitializer getCommandInitializerService() {
82 return commandInitializerService;
83 }
84
85 public void setCommandInitializerService(
86 ICommandInitializer commandInitializerService) {
87 this.commandInitializerService = commandInitializerService;
88 }
89
90 public List<Face> getFaces() {
91 return faces;
92 }
93
94 public void setFaces(List<Face> faces) {
95 this.faces = faces;
96 }
97
98 public void init() {
99
100
101 commandManager = new CommandManager();
102
103 for(CommandManagerListenerContribution contrib : commandManagerListeners) {
104 if(contrib.getHoverListener() != null) {
105 commandManager.addHoverListener(contrib.getHoverListener());
106 }
107 if(contrib.getManagerListener() != null) {
108 commandManager.addCommandManagerListener(contrib.getManagerListener());
109 }
110 }
111
112 CommandFactory commandFactory = new CommandFactory();
113 for(CommandDescription desc : commandDescriptions) {
114
115 ISiouxsieCommand command = commandFactory.createCommand(desc);
116
117
118 command.setExceptionHandler(getExceptionHandler());
119
120 getCommandInitializerService().initialize(command.getCommand());
121
122 if(desc.getAppId() != null) {
123 apps.add(desc.getAppId());
124 }
125 siouxsieCommands.put(desc.getId(),command.getCommand());
126 }
127
128 Map<String, String> commandIdToGroup = new HashMap<String, String>();
129
130 for(Face face : faces) {
131
132
133 ActionCommand command = getCommand(face.getCommandId());
134 if(command == null) {
135 LOG.warn("Cannot retrieve command "+face.getCommandId()+" from one of its faces.");
136 }
137 org.pietschy.command.Face faceImpl = null;
138 if(face.getName() != null) {
139 faceImpl = command.getFace(face.getName(), true);
140 } else {
141 faceImpl = command.getDefaultFace(true);
142 }
143
144 faceImpl.setText(face.getText());
145 faceImpl.setDescription(face.getDescription());
146 faceImpl.setLongDescription(face.getLongDescription());
147 if(face.getIconPath() != null) {
148 faceImpl.setIcon(createIcon(face.getIconType(),face.getIconPath()));
149 }
150
151 if(face.getGroupId() != null) {
152 commandIdToGroup.put(face.getCommandId(), face.getGroupId());
153 }
154 if(face.getAccelerator() != null) {
155 faceImpl.setAccelerator(KeyStroke.getKeyStroke(face.getAccelerator()));
156 }
157
158 if(face.getSeparator() != null) {
159 if(face.isSeparatorOk()) {
160 if(face.separatorBefore()) {
161 faceImpl.putClientProperty(FACE_SEP_BEFORE_KEY, Boolean.TRUE.toString());
162 }
163 if(face.separatorAfter()) {
164 faceImpl.putClientProperty(FACE_SEP_AFTER_KEY, Boolean.TRUE.toString());
165 }
166 } else {
167 LOG.warn("incorrect separator syntax '"+face.getSeparator()+"' for face "
168 +face.getName()+" of action "+face.getCommandId()+
169 ". Separator ignored.");
170
171 }
172 }
173
174 }
175
176 Map<String,ApplicationMenuBarContainer> appsMenuBar = new LinkedHashMap<String, ApplicationMenuBarContainer>();
177 Map<String, CommandGroup> appIdToToolBar = new LinkedHashMap<String,CommandGroup>();
178
179
180 apps.add(DEFAULT_APP);
181 for(String app : apps) {
182
183 ApplicationMenuBarContainer menuBarContainer = new ApplicationMenuBarContainer();
184
185 appsMenuBar.put(app, menuBarContainer);
186
187
188
189 for(Group group : groups) {
190 LOG.info("processing group "+group.getId()+" for app "+app);
191 if(menuBarContainer.isMainMenu(group)) {
192 LOG.info("group is a main group");
193 menuBarContainer.addMainMenu(group, app+"."+group.getId());
194 } else {
195 menuBarContainer.addMenu(group, app+"."+group.getId());
196 }
197 }
198
199 for(Group group : groups) {
200 boolean isMainMenu = menuBarContainer.isMainMenu(group.getId());
201 LOG.info("is "+group.getId()+" main menu? "+isMainMenu);
202 if(!isMainMenu) {
203
204 CommandGroup parent = menuBarContainer.getMenu(group.getGroupId());
205 if(parent == null) {
206 LOG.warn("cannot retrieve parent group "+group.getGroupId()+" for group "+group.getId());
207 LOG.warn("subgroup ignored");
208 } else {
209 parent.add(menuBarContainer.getMenu(group.getId()));
210 }
211 }
212 }
213
214 CommandGroup groupToolBar = new CommandGroup(commandManager,app+"."+org.pietschy.command.Face.TOOLBAR);
215
216 groupToolBar.getFace(org.pietschy.command.Face.TOOLBAR,true);
217 appIdToToolBar.put(app, groupToolBar);
218 }
219
220
221 for(CommandDescription desc : commandDescriptions) {
222
223
224 Collection<CommandGroup> toolBarList;
225
226 Map<String,ApplicationMenuBarContainer> commandGroupList;
227
228
229 if(desc.getAppId() == null) {
230
231 commandGroupList = appsMenuBar;
232 toolBarList = appIdToToolBar.values();
233 } else {
234
235
236 commandGroupList = new HashMap<String, ApplicationMenuBarContainer>();
237 commandGroupList.put(desc.getAppId(), appsMenuBar.get(desc.getAppId()));
238
239 CommandGroup appToolBar = appIdToToolBar.get(desc.getAppId());
240 toolBarList = new ArrayList<CommandGroup>();
241 toolBarList.add(appToolBar);
242 }
243
244 Command command = idToCommand.get(desc.getId());
245
246
247 for(ApplicationMenuBarContainer container : commandGroupList.values()) {
248 String groupId = commandIdToGroup.get(desc.getId());
249 if(groupId != null) {
250 CommandGroup group = container.getMenu(groupId);
251 if(command.getDefaultFace().getClientProperty(FACE_SEP_BEFORE_KEY) != null) {
252 group.addSeparator();
253 }
254 group.add(command);
255 if(command.getDefaultFace().getClientProperty(FACE_SEP_AFTER_KEY) != null) {
256 group.addSeparator();
257 }
258 }
259 }
260
261
262 if(command.faceExists(org.pietschy.command.Face.TOOLBAR)) {
263 for(CommandGroup group : toolBarList) {
264 if(command.getFace(org.pietschy.command.Face.TOOLBAR).getClientProperty(FACE_SEP_BEFORE_KEY) != null) {
265 group.addSeparator();
266 }
267 group.add(command);
268 if(command.getFace(org.pietschy.command.Face.TOOLBAR).getClientProperty(FACE_SEP_AFTER_KEY) != null) {
269 group.addSeparator();
270 }
271 }
272 }
273 }
274
275
276 for(String appId : appsMenuBar.keySet()) {
277 ApplicationMenuBarContainer container = appsMenuBar.get(appId);
278 CommandGroup menuBarGroup = new CommandGroup(commandManager,appId+".menubar");
279 for(CommandGroup group : container.mainMenus.values()) {
280 if(group.getMemberCount() > 0) {
281 menuBarGroup.add(group);
282 }
283 }
284
285
286 for(Group group : container.groups.values()) {
287 if(!container.isMainMenu(group)) {
288 CommandGroup gImpl = container.getMenu(group.getId());
289 if(gImpl.getMemberCount() == 0) {
290
291 CommandGroup parent = container.getMenu(group.getGroupId());
292 parent.remove(gImpl);
293 }
294 }
295 }
296 appsMenuBars.put(appId, menuBarGroup.createMenuBar());
297 }
298
299 for(String appId : appIdToToolBar.keySet()) {
300 CommandGroup toolBarGroup = appIdToToolBar.get(appId);
301 appsToolBars.put(appId, toolBarGroup.createToolBar());
302 }
303
304
305 }
306
307 public List<CommandDescription> getCommandDescriptions() {
308 return commandDescriptions;
309 }
310
311 public void setCommandDescriptions(List<CommandDescription> commandDescriptions) {
312 this.commandDescriptions = commandDescriptions;
313 }
314
315 public List<Group> getGroups() {
316 return groups;
317 }
318
319 public void setGroups(List<Group> groups) {
320 this.groups = groups;
321 }
322
323 private Icon createIcon(String type,String path) {
324 Icon icon = null;
325 if("url".equals(type)) {
326 try {
327 icon = new ImageIcon(new URL(path));
328 } catch (MalformedURLException e) {
329 LOG.error("Could not load icon.",e);
330 }
331 } else if("classpath".equals(type)) {
332 icon = new ImageIcon(commandManager.getClassLoader().getResource(path));
333 } else if("file".equals(type)) {
334 icon = new ImageIcon(path);
335 }
336 return icon;
337 }
338
339 /**
340 * Return a command from its id.
341 * @param commandId
342 * @return
343 */
344 public ActionCommand getCommand(String commandId) {
345 return idToCommand.get(commandId);
346 }
347
348 public JMenuBar getAppMenuBar(String appId) {
349 JMenuBar bar = appsMenuBars.get(appId);
350 if(bar == null) {
351 bar = appsMenuBars.get(DEFAULT_APP);
352 }
353 return bar;
354 }
355
356 public JToolBar getAppToolBar(String appId) {
357 JToolBar bar = appsToolBars.get(appId);
358 if(bar == null) {
359 bar = appsToolBars.get(DEFAULT_APP);
360 }
361 return bar;
362 }
363
364 public ICommand getSiouxsieCommand(String id) {
365 return siouxsieCommands.get(id);
366 }
367
368 public List<CommandManagerListenerContribution> getCommandManagerListeners() {
369 return commandManagerListeners;
370 }
371
372 public void setCommandManagerListeners(
373 List<CommandManagerListenerContribution> commandManagerListeners) {
374 this.commandManagerListeners = commandManagerListeners;
375 }
376
377 public IExceptionHandler getExceptionHandler() {
378 return exceptionHandler;
379 }
380
381 public void setExceptionHandler(IExceptionHandler exceptionHandler) {
382 this.exceptionHandler = exceptionHandler;
383 }
384
385 /**
386 * Helper class for dealing with groups and sub groups.
387 * @author Arnaud Cogoluegnes
388 * @version $Id$
389 */
390 class ApplicationMenuBarContainer {
391
392 private Map<String,CommandGroup> mainMenus = new LinkedHashMap<String, CommandGroup>();
393
394 private Map<String,CommandGroup> allMenus = new LinkedHashMap<String, CommandGroup>();
395
396 private Map<String, Group> groups = new LinkedHashMap<String, Group>();
397
398 public void addMainMenu(Group group, String guiCommandId) {
399 CommandGroup gImpl = constructCommandGroup(group, guiCommandId);
400 mainMenus.put(group.getId(), gImpl);
401 groups.put(group.getId(), group);
402 allMenus.put(group.getId(), gImpl);
403 }
404
405 public void addMenu(Group group, String guiCommandId) {
406 CommandGroup gImpl = constructCommandGroup(group, guiCommandId);
407 groups.put(group.getId(), group);
408 allMenus.put(group.getId(), gImpl);
409 }
410
411 private CommandGroup constructCommandGroup(Group group, String guiCommandId ) {
412 CommandGroup groupImpl = new CommandGroup(commandManager,guiCommandId);
413 org.pietschy.command.Face face = groupImpl.getDefaultFace(true);
414 face.setText(group.getText());
415 face.setDescription(group.getDescription());
416 face.setLongDescription(group.getLongDescription());
417 if(group.getIconPath() != null) {
418 face.setIcon(createIcon(group.getIconType(),group.getIconPath()));
419 }
420 return groupImpl;
421 }
422
423 public CommandGroup getMenu(String id) {
424 return allMenus.get(id);
425 }
426
427 public boolean isMainMenu(String id) {
428 Group group = groups.get(id);
429 return group != null && isMainMenu(group);
430 }
431
432 public boolean isMainMenu(Group group) {
433 return group.getGroupId() == null || group.getGroupId().trim().length() == 0;
434 }
435
436 }
437
438 class CommandFactory {
439
440 public ISiouxsieCommand createCommand(CommandDescription desc) {
441
442 ICommand command = null;
443 if(desc.getCommand() != null) {
444 command = desc.getCommand();
445 } else {
446 try {
447 command = desc.getCommandClass().newInstance();
448 } catch (InstantiationException e) {
449 LOG.error("Could not create command "+desc.getId(),e);
450 } catch (IllegalAccessException e) {
451 LOG.error("Could not create command "+desc.getId(),e);
452 }
453 }
454
455
456
457 ISiouxsieCommand res = null;
458 if(command instanceof IToggleCommand) {
459
460 res = new SiouxsieToggleCommand(commandManager,desc.getId());
461
462 idToCommand.put(desc.getId(), (SiouxsieToggleCommand) res);
463 ((SiouxsieToggleCommand)res).setCommand((IToggleCommand)command);
464 if(desc.getAccelerator() != null) {
465 ((SiouxsieToggleCommand)res).getDefaultFace(true).setAccelerator(
466 KeyStroke.getKeyStroke(desc.getAccelerator())
467 );
468 }
469 } else {
470
471 res = new SiouxsieActionCommand(commandManager,desc.getId());
472
473 idToCommand.put(desc.getId(), (SiouxsieActionCommand) res);
474 ((SiouxsieActionCommand)res).setCommand(command);
475 if(desc.getAccelerator() != null) {
476 ((SiouxsieActionCommand)res).getDefaultFace(true).setAccelerator(
477 KeyStroke.getKeyStroke(desc.getAccelerator())
478 );
479 }
480 }
481 return res;
482 }
483
484 }
485
486 }