New Jide License
[indepmod/experimental.git] / IndependentModeler / src / cz / cvut / promod / services / menuService / MenuControlServiceImpl.java
blob5fc70934162655ce2835bbc44661c66e8eb0482e
1 package cz.cvut.promod.services.menuService;
3 import cz.cvut.promod.gui.ModelerModel;
4 import cz.cvut.promod.services.ModelerSession;
5 import cz.cvut.promod.services.actionService.actionUtils.ProModAction;
6 import cz.cvut.promod.services.menuService.utils.*;
7 import org.apache.log4j.Logger;
9 import javax.swing.*;
10 import java.awt.*;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.LinkedList;
14 import java.util.List;
16 /**
17 * ProMod, master thesis project
18 * User: Petr Zverina, petr.zverina@gmail.com
19 * Date: 17:49:26, 10.10.2009
22 /**
23 * Implementation of MenuService and MenuControlService.
25 public class MenuControlServiceImpl implements MenuControlService {
27 private final Logger LOG = Logger.getLogger(MenuControlServiceImpl.class);
29 private final JPopupMenu projectTreePopupMenu;
30 private final JMenuBar menuBar;
31 private final MenuValidator menuValidator;
33 public MenuControlServiceImpl(){
34 projectTreePopupMenu = ModelerSession.getComponentFactoryService().createPopupMenu();
35 menuBar = ModelerSession.getComponentFactoryService().createMenuBar();
36 menuValidator = new MenuValidator();
39 /** {@inheritDoc} */
40 public boolean check() {
41 return true;
44 /** {@inheritDoc} */
45 public JMenuBar getMenuBar(){
46 return menuBar;
49 /** {@inheritDoc} */
50 public JPopupMenu getProjectTreePopupMenu() {
51 return projectTreePopupMenu;
54 /** {@inheritDoc} */
55 public InsertMenuItemResult insertProjectNavigationPopupMenuItem(final ProModAction proModAction,
56 final MenuItemPosition menuItemPosition) {
58 return insertProjectNavigationPopupMenuItem(
59 proModAction,
60 menuItemPosition,
61 MenuSeparator.NONE
65 /** {@inheritDoc} */
66 public InsertMenuItemResult insertProjectNavigationPopupMenuItem(final ProModAction proModAction,
67 final MenuItemPosition menuItemPosition,
68 final MenuSeparator menuSeparator) {
70 return registerPopupMenuItem(
71 ModelerModel.MODELER_IDENTIFIER,
72 proModAction,
73 menuItemPosition,
74 menuSeparator
78 /** {@inheritDoc} */
79 public InsertMenuItemResult insertPopupMenuItem(final String notationIdentifier,
80 final ProModAction proModAction,
81 final MenuItemPosition menuItemPosition){
83 return insertPopupMenuItem(
84 notationIdentifier,
85 proModAction,
86 menuItemPosition,
87 MenuSeparator.NONE
91 /** {@inheritDoc} */
92 public InsertMenuItemResult insertPopupMenuItem(final String notationIdentifier,
93 final ProModAction proModAction,
94 final MenuItemPosition menuItemPosition,
95 final MenuSeparator menuSeparator) {
97 return registerPopupMenuItem(
98 notationIdentifier,
99 proModAction,
100 menuItemPosition,
101 menuSeparator
106 * Inserts the popup menu item.
108 * @param notationIdentifier is the identifier of notation
109 * @param proModAction is the action to be added
110 * @param menuItemPosition is the menu items required position
111 * @param menuSeparator true if the separator should be added
112 * @return an value of InsertMenuItemResult enum saying what was the the operation result
114 private InsertMenuItemResult registerPopupMenuItem(final String notationIdentifier,
115 final ProModAction proModAction,
116 final MenuItemPosition menuItemPosition,
117 final MenuSeparator menuSeparator) {
120 JPopupMenu popupMenu;
122 if(ModelerModel.MODELER_IDENTIFIER.equals(notationIdentifier)){
123 popupMenu = projectTreePopupMenu;
125 if (menuValidator.isPopupSupported(notationIdentifier, popupMenu)) return InsertMenuItemResult.POPUP_NOT_SUPPORTED;
127 if (menuValidator.isMenuItemPositionSet(menuItemPosition)) return InsertMenuItemResult.WRONG_PLACEMENT;
129 final String hierarchicalPlacement = menuItemPosition.getHierarchicalPlacement();
130 final String[] placementParts;
132 if(hierarchicalPlacement != null && !hierarchicalPlacement.isEmpty()){
133 placementParts = hierarchicalPlacement.split(PLACEMENT_DELIMITER);
134 } else {
135 placementParts = new String[0];
138 // get all menus of popupMenuItems
139 final JMenuItem[] popupMenuItems = new JMenuItem[popupMenu.getComponentCount()];
140 for(int i = 0; i < popupMenu.getComponentCount(); i++){
141 popupMenuItems[i] = (JMenuItem) popupMenu.getComponent(i);
144 return insertIntoMenu(
145 popupMenuItems,
146 placementParts,
147 proModAction,
148 null,
149 null,
150 popupMenu,
151 menuSeparator,
152 menuItemPosition,
153 false
157 if(ModelerSession.getNotationService().existNotation(notationIdentifier)){
158 return ModelerSession.getNotationService().getNotation(notationIdentifier).addPopupMenuItem(proModAction, menuItemPosition, menuSeparator, false);
159 } else {
160 LOG.error("No existing notation cannot provide it's popup menu.");
161 return null;
170 /** {@inheritDoc} */
171 public InsertMenuItemResult insertMainMenuItem(final ProModAction proModAction,
172 final MenuItemPosition menuItemPosition,
173 final boolean checkable){
175 return insertMainMenuItem(proModAction, menuItemPosition, MenuSeparator.NONE, checkable);
178 /** {@inheritDoc} */
179 public InsertMenuItemResult insertMainMenuItem(final ProModAction proModAction,
180 final MenuItemPosition menuItemPosition,
181 final MenuSeparator menuSeparator,
182 final boolean checkable){
184 if(!menuValidator.isMenuItemPositionSet(menuItemPosition)) {return InsertMenuItemResult.WRONG_PLACEMENT;}
186 final String[] placementParts = menuItemPosition.getHierarchicalPlacement().split(PLACEMENT_DELIMITER);
188 if(!menuValidator.areValidPlacementParts(placementParts)) {return InsertMenuItemResult.WRONG_PLACEMENT;}
190 if (!menuValidator.isItemLocationValid(placementParts)) return InsertMenuItemResult.WRONG_PLACEMENT;
192 // get all menus of menuBar
193 final JMenuItem[] menuBarItems = new JMenuItem[menuBar.getMenuCount()];
194 for(int i = 0; i < menuBar.getMenuCount(); i++){
195 menuBarItems[i] = menuBar.getMenu(i);
198 return insertIntoMenu(
199 menuBarItems,
200 placementParts,
201 proModAction,
202 null,
203 menuBar,
204 null,
205 menuSeparator,
206 menuItemPosition,
207 checkable
214 /** {@inheritDoc} */
215 public InsertMenuItemResult insertMainMenuItem(final ProModAction proModAction,
216 final MenuItemPosition menuItemPosition){
218 return insertMainMenuItem(
219 proModAction,
220 menuItemPosition,
221 false);
224 /** {@inheritDoc} */
225 public InsertMenuItemResult insertMainMenuItem(final ProModAction proModAction,
226 final MenuItemPosition menuItemPosition,
227 final MenuSeparator menuSeparator){
229 return insertMainMenuItem(
230 proModAction,
231 menuItemPosition,
232 menuSeparator,
233 false);
237 * @param menu parent
238 * @return items in the parent menu
240 private JMenuItem[] getMenuItems(final JMenu menu){
241 final ArrayList<JMenuItem> array = new ArrayList<JMenuItem>();
243 for(int i = 0; i < menu.getItemCount(); i++){
244 final Object object = menu.getItem(i);
245 if((object != null)){
246 array.add(menu.getItem(i));
250 return array.toArray(new JMenuItem[array.size()]);
254 * Inserts the menu action.
256 * @param parentMenu is the parent menu where the action is supposed to be added
257 * @param parentPopupMenu is the parent popup menu where the action is supposed to be added
258 * @param proModAction is the action to be inserted
259 * @param menuSeparator the separator if one is supposed to be inserted
260 * @param menuItemPosition required menu item position
261 * @param checkable true if the menu item is supposed to be checkable
262 * @return true if no error occurs, false otherwise
264 public InsertMenuItemResult insertAction(final JMenu parentMenu,
265 final JPopupMenu parentPopupMenu,
266 final ProModAction proModAction,
267 final MenuSeparator menuSeparator,
268 final MenuItemPosition menuItemPosition,
269 final boolean checkable){
271 // just one parent can be used
272 if(!menuValidator.itemHasOnlyOneParent(parentMenu, parentPopupMenu)){
273 throw new IllegalArgumentException("Obscure parent menu item.");
276 if (!menuValidator.isActionDefined(proModAction)){ return InsertMenuItemResult.UNDEFINED_ACTION; }
278 final JMenuItem menuItem = (JMenuItem) getMenuItem(checkable);
279 menuItem.setAction(proModAction);
281 // check whether the is no same menu item in the this menu structure level
282 final List<ModelerMenuItem> relatives =
283 getRelatedMenuItems(parentPopupMenu, parentMenu, (String) proModAction.getValue(Action.NAME));
285 if(relatives != null){
286 LOG.debug("Inserting a menu item to the menu hierarchy where a menu item with the same name (" +
287 proModAction.getValue(Action.NAME) + ") has already been inserted.");
289 if(menuItem instanceof ModelerMenuItem){
290 ((ModelerMenuItem) menuItem).setListOfRelatives(relatives);
292 // if there are any relatives, always group them
293 menuItemPosition.getPositionInfo().setRelativePosition(MenuItemPosition.PlacementStyle.GROUPED);
297 if(!menuValidator.isUniqueAction(parentMenu, parentPopupMenu, proModAction)){
298 return InsertMenuItemResult.DUPLICIT_ACTION;
301 int insertionPosition = insertItem(menuItemPosition, menuItem, parentMenu, parentPopupMenu);
302 if(insertionPosition < 0){
303 return InsertMenuItemResult.WRONG_PLACEMENT;
306 // if items are grouped, only one separator can be inserted before and one after the group
307 // this statement deals with inserting of separators AFTER the group
308 if(MenuSeparator.AFTER.equals(menuSeparator)){
309 if(relatives != null && relatives.size() > 0){ // grouping active
310 insertionPosition += relatives.size() + 1;
314 insertSeparator(menuSeparator, parentMenu, parentPopupMenu, insertionPosition);
316 return InsertMenuItemResult.SUCCESS;
323 * Inserts a menu separator to the required position. Never inserts two separators next to each other.
325 * @param menuSeparator hold information whether insert separator or not and the relative position
326 * @param parentMenu is the parent menu item
327 * @param parentPopupMenu is the parent popup menu item
328 * @param insertionPosition is the actual position of the menu item that has been just added and to which is the
329 * relative position of the separator specified
331 private void insertSeparator(final MenuSeparator menuSeparator,
332 final JMenu parentMenu,
333 final JPopupMenu parentPopupMenu,
334 final int insertionPosition) {
336 // do not insert two separator next to each other
337 final Component[] components;
338 if(parentMenu != null){
339 components = parentMenu.getMenuComponents();
340 } else if(parentPopupMenu != null){
341 components = parentPopupMenu.getComponents();
342 } else {
343 LOG.error("Obscurity in menu parents.");
344 return;
347 if(menuValidator.isSeparatorAlreadyDefinedAtPosition(components, insertionPosition, menuSeparator)){
348 LOG.info("Skipping insertion of menu separator.");
349 return;
352 // insert line separator before, if obtained
353 if(MenuSeparator.BEFORE.equals(menuSeparator)){
354 if(parentMenu != null) {
355 parentMenu.insertSeparator(insertionPosition);
356 } else{
357 parentPopupMenu.insert(new JPopupMenu.Separator(), insertionPosition);
361 // insert line separator after, if obtained
362 if(MenuSeparator.AFTER.equals(menuSeparator)){
363 if(parentMenu != null) {
364 parentMenu.insertSeparator(insertionPosition + 1);
365 } else {
366 parentPopupMenu.insert(new JPopupMenu.Separator(), insertionPosition + 1);
373 * Performs the actual insertion of menu item.
375 * @param menuItemPosition is the specification of item's position
376 * @param menuItem is the item to be inserted
377 * @param parentMenu is the parent menu
378 * @param parentPopupMenu is the parent popupMenu
379 * @return the position to that has been the new menu item inserted
381 private int insertItem(final MenuItemPosition menuItemPosition,
382 final JMenuItem menuItem,
383 final JMenu parentMenu,
384 final JPopupMenu parentPopupMenu) {
386 int insertionPosition = -1;
388 //insert menuItem to the position specified (exact position, FIRST, LAST possibilities)
389 if(MenuItemPosition.PlacementStyle.GROUPED.equals(menuItemPosition.getPlacementStyle())){
390 final Component[] parentMenuItems;
392 if (!menuValidator.itemHasOnlyOneParent(parentMenu, parentPopupMenu)){
393 return -1;
396 if(parentMenu != null && parentPopupMenu == null){
397 parentMenuItems = parentMenu.getMenuComponents();
399 else { //(parentMenu == null && parentPopupMenu != null)
400 parentMenuItems = parentPopupMenu.getComponents();
404 final int position = groupMenuItem(menuItem, parentMenuItems);
405 if(parentMenu != null){
406 parentMenu.insert(menuItem, position);
407 } else {
408 parentPopupMenu.insert(menuItem, position);
411 insertionPosition = position;
413 else if(menuItemPosition.getPosition() >= 0){
414 if(parentMenu != null) {
415 final int position = Math.min(menuItemPosition.getPosition(), parentMenu.getItemCount());
416 parentMenu.insert(menuItem, position);
417 insertionPosition = position;
419 else if(parentPopupMenu != null) {
420 final int position = Math.min(menuItemPosition.getPosition(), parentPopupMenu.getComponentCount());
421 parentPopupMenu.insert(menuItem, position);
422 insertionPosition = position;
424 } else {
425 if(MenuItemPosition.PlacementStyle.FIRST.equals(menuItemPosition.getPlacementStyle())){
426 if(parentMenu != null) { parentMenu.insert(menuItem, 0); }
427 else if(parentPopupMenu != null) { parentPopupMenu.insert(menuItem, 0); }
429 insertionPosition = 0;
431 } else if(MenuItemPosition.PlacementStyle.LAST.equals(menuItemPosition.getPlacementStyle())){
432 if(parentMenu != null) {
433 parentMenu.add(menuItem);
434 insertionPosition = parentMenu.getMenuComponentCount() -1;
436 else if(parentPopupMenu != null) {
437 parentPopupMenu.add(menuItem);
438 insertionPosition = parentPopupMenu.getComponentCount() - 1;
443 return insertionPosition;
447 * Finds the menu item(s) having the same text under the same parent menu item and returns the position
448 * (first possible) whether the new menu item is supposed to be inserted.
450 * @param menuItem is the menu item that is supposed to be inserted
451 * @param parentMenuItems are all current menu items
452 * @return the position where the new menu item is supposed to be inserted
454 private int groupMenuItem(final JMenuItem menuItem, final Component[] parentMenuItems) {
455 for(int i = 0; i < parentMenuItems.length; i++){
456 final Component component = parentMenuItems[i];
458 if(component instanceof JMenuItem){
459 final JMenuItem item = (JMenuItem) component;
461 if(item instanceof ModelerMenuItem && item.getText().equalsIgnoreCase(menuItem.getText())){
462 return i;
467 return parentMenuItems.length;
470 private ModelerMenuItem getMenuItem(final boolean checkable) {
471 if(checkable){
472 return new CheckableMenuItem();
473 } else {
474 return new DefaultMenuItem();
479 * Finds the duplicity in menu item names and prepare the 'relatives' mechanism.
480 * If there are more than one item having the same text on it, then the set if relatives is set to an empty
481 * set. The last item having the same text on it have this set filled by references on the previous items
482 * having the same text (relatives). All menu items that do not have any duplicity in text have this set set
483 * to null.
485 * @param parentPopupMenu is the parent popup menu item
486 * @param parentMenu is the parent menu item
487 * @param displayName is the text that is supposed to be displayed on the menu item
488 * @return the set holding references to the relatives, null if there is no text duplicity
489 * @throws IllegalArgumentException when an obscurity in parent occurs
491 private List<ModelerMenuItem> getRelatedMenuItems(final JPopupMenu parentPopupMenu,
492 final JMenu parentMenu,
493 final String displayName) throws IllegalArgumentException{
495 if(!menuValidator.itemHasOnlyOneParent(parentMenu, parentPopupMenu)){
496 throw new IllegalArgumentException("Obscure parent menu item.");
499 final Component[] menuComponents;
500 if(parentPopupMenu != null){
501 menuComponents = parentPopupMenu.getComponents();
502 } else {
503 assert parentMenu != null;
504 menuComponents = parentMenu.getMenuComponents();
507 List<ModelerMenuItem> relatives = null;
509 for(final Component component : menuComponents){
510 if(component instanceof JMenuItem){
511 final JMenuItem menuItem = (JMenuItem) component;
513 if(displayName.equals(menuItem.getText())){
514 if(menuItem instanceof ModelerMenuItem){
515 final ModelerMenuItem modelerMenuItem = (ModelerMenuItem) menuItem;
517 relatives = modelerMenuItem.getListOfRelatives();
518 modelerMenuItem.setListOfRelatives(new LinkedList<ModelerMenuItem>());
520 if(relatives == null){
521 relatives = new LinkedList<ModelerMenuItem>();
524 relatives.add(modelerMenuItem);
530 return relatives;
534 * Inserts the menu item.
536 * @param menuLevelItems is the parent menu item
537 * @param placementParts is the hierarchical position
538 * @param proModAction is the action to be inserted
539 * @param parentMenu menu parent
540 * @param parentMenuBar menu bar
541 * @param parentPopupMenu parent popup menu
542 * @param menuSeparator menu separator info
543 * @param menuItemPosition menu item position info
544 * @param checkable true if the action is supposed to be checkable
545 * @return true id no error occurred, false otherwise
547 private InsertMenuItemResult insertIntoMenu(
548 final JMenuItem[] menuLevelItems,
549 final String[] placementParts,
550 final ProModAction proModAction,
551 final JMenu parentMenu,
552 final JMenuBar parentMenuBar,
553 final JPopupMenu parentPopupMenu,
554 final MenuSeparator menuSeparator,
555 final MenuItemPosition menuItemPosition,
556 final boolean checkable) {
558 // parent has to be clear
559 if (!menuValidator.hasCorrectParentMenu(parentMenu, parentMenuBar, parentPopupMenu)) {
560 throw new IllegalArgumentException("Obscure parent menu item.");
563 if (!menuValidator.isActionDefined(proModAction)) return InsertMenuItemResult.UNDEFINED_ACTION;
565 if(!ModelerSession.getActionControlService().isRegisteredAction(proModAction)){
566 LOG.error("Unregistered action, action identifier: " + proModAction.getActionIdentifier());
567 return InsertMenuItemResult.NOT_REGISTERED_ACTION;
570 if(placementParts.length == 0){
571 if(parentMenu != null){
572 return insertAction(parentMenu, null, proModAction, menuSeparator, menuItemPosition, checkable);
573 } else if (parentPopupMenu != null){
574 return insertAction(null, parentPopupMenu, proModAction, menuSeparator, menuItemPosition, checkable);
575 } else {
576 LOG.error("No parent set for the menu item.");
577 return InsertMenuItemResult.WRONG_PLACEMENT;
579 } else {
580 for (final JMenuItem menuItem : menuLevelItems) {
581 if (menuItem.getText().equals(placementParts[0])) {
582 if (menuItem instanceof JMenu) {
583 final JMenu menu = (JMenu) menuItem;
585 final JMenuItem[] nextMenuItems = getMenuItems(menu);
587 return insertIntoMenu(
588 nextMenuItems,
589 Arrays.copyOfRange(placementParts, 1, placementParts.length),
590 proModAction,
591 menu,
592 null,
593 null,
594 menuSeparator,
595 menuItemPosition,
596 checkable
602 JMenu parent = parentMenu;
603 for (final String placementPart : placementParts) {
604 final JMenu menu = ModelerSession.getComponentFactoryService().createMenu(placementPart);
606 if (parent != null) {
607 parent.add(menu);
608 } else if (parentMenuBar != null) {
609 parentMenuBar.add(menu);
610 } else if (parentPopupMenu != null) {
611 parentPopupMenu.add(menu);
612 } else {
613 LOG.error("There is no parent set to build the menu structure.");
614 throw new IllegalArgumentException("Obscure parent menu item.");
617 parent = menu;
620 return insertAction(parent, null, proModAction, menuSeparator, menuItemPosition, checkable);