OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.erp.config;

import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.group.Item;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Action;

import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

@ThreadSafe
public final class MenuAndActions {

    @GuardedBy("this")
    private final Group group;
    @GuardedBy("this")
    private final Map<String, Action> actions;

    public MenuAndActions() {
        this(new Group("menu.main"), Collections.<String, Action> emptyMap());
    }

    public MenuAndActions(final Group group, final Map<String, Action> actions) {
        this.group = Group.copy(group, null);
        this.actions = new HashMap<>(actions);
    }

    public final synchronized MenuAndActions copy() {
        return new MenuAndActions(this.getGroup(), this.actions);
    }

    public final void putAction(final Action a) {
        this.putAction(a, null);
    }

    public final void putAction(final Action a, final String id) {
        this.putAction(a, id, false);
    }

    /**
     * Add an action.
     * 
     * @param a the action to add.
     * @param id the ID, if <code>null</code> taken from the
     *        {@link SwingThreadUtils#getActionID(Action) action}.
     * @param canReplace <code>true</code> if the passed action can replace an existing one.
     * @return the used ID.
     */
    public final String putAction(final Action a, final String id, final boolean canReplace) {
        return this.putAction(a, id, canReplace, false);
    }

    private final synchronized String putAction(final Action a, String id, final boolean canReplace, final boolean dryRun) {
        if (a == null)
            throw new NullPointerException("Null action");
        if (id == null)
            id = SwingThreadUtils.getActionID(a);
        if (id == null)
            throw new NullPointerException("Null ID");
        if (!canReplace && this.actions.containsKey(id))
            throw new IllegalStateException("ID exists : " + this.actions.get(id));
        if (!dryRun)
            this.actions.put(id, a);
        return id;
    }

    public final synchronized Action getAction(final String id) {
        return this.actions.get(id);
    }

    /**
     * Return the group modeling the menu.
     * 
     * @return a frozen group.
     */
    public final synchronized Group getGroup() {
        return this.group;
    }

    public void addMenuItem(final Action action, final List<String> path) {
        this.addMenuItem(action, null, path);
    }

    public void addMenuItem(final Action action, final String actionID, final List<String> path) {
        this.addMenuItem(action, actionID, path, false);
    }

    /**
     * Adds a menu item to this menu. The path should be an alternation of menu and group within
     * that menu. All items within the same group will be grouped together inside separators. Menus
     * will be created as needed.
     * 
     * @param action the action to perform.
     * @param actionID ID of the action, see {@link #putAction(Action, String, boolean)}.
     * @param path where to add the menu item.
     * @param canReplace <code>true</code> if this method can replace an existing action and menu
     *        item.
     * @return the menu item.
     * @throws IllegalArgumentException if path is empty.
     * @throws IllegalStateException if <code>actionID</code> already exists in either the menu or
     *         the actions.
     */
    public Item addMenuItem(final Action action, String actionID, final List<String> path, final boolean canReplace) throws IllegalStateException {
        if (path.isEmpty())
            throw new IllegalArgumentException("Empty path");

        final Item res;
        synchronized (this) {
            // check actionID
            actionID = this.putAction(action, actionID, canReplace, true);

            // check and modify group
            final Group groupDesc = this.getGroup().followPath(path, true);
            final Item child = groupDesc.getDescFromID(actionID);
            if (!canReplace && child != null) {
                throw new IllegalStateException("ID exists : " + child);
            }
            if (child == null) {
                res = new Item(actionID);
                groupDesc.add(res);
            } else {
                res = child;
            }

            // modify action
            this.putAction(action, actionID, canReplace, false);
        }
        assert res != null;
        return res;
    }

    public void setMenuItemVisible(final String actionID, final boolean v) {
        synchronized (this) {
            final Item mi = this.getGroup().getDescFromID(actionID);
            mi.setLocalHint(mi.getLocalHint().getBuilder().setVisible(v).build());
        }
    }
}