org.waveprotocol.wave.client.widget.menu.PopupMenu.java Source code

Java tutorial

Introduction

Here is the source code for org.waveprotocol.wave.client.widget.menu.PopupMenu.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.waveprotocol.wave.client.widget.menu;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.NotStrict;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

import org.waveprotocol.wave.client.common.webdriver.DebugClassHelper;
import org.waveprotocol.wave.client.widget.popup.AlignedPopupPositioner;
import org.waveprotocol.wave.client.widget.popup.PopupChromeFactory;
import org.waveprotocol.wave.client.widget.popup.PopupEventListener;
import org.waveprotocol.wave.client.widget.popup.PopupFactory;
import org.waveprotocol.wave.client.widget.popup.RelativePopupPositioner;
import org.waveprotocol.wave.client.widget.popup.UniversalPopup;
import org.waveprotocol.wave.model.util.Pair;

/**
 *
 * Desktop version for rendering a vertical sub-menu.
 * I don't like it any more than you do.
 */
public class PopupMenu implements Menu {
    interface Resources extends ClientBundle {
        /** CSS class names used by DesktopSubMenu. These are used in Menu.css */
        interface Css extends CssResource {
            String item();

            String disabled();

            String divider();
        }

        @Source("Menu.css")
        @NotStrict // TODO(user): make Strict by including/inheriting "verticalSeparator"
        Css css();
    }

    /**
     * An implementation of {@code MenuItem} for a {@code PopupMenu}.
     *
     */
    private class PopupMenuItem extends Label implements MenuItem {
        /**
         * Whether the default state of the item is enabled or not.
         */
        private final boolean defaultEnabled;

        /**
         * Whether the item is enabled or not.
         */
        private boolean enabled;

        /**
         * The command to execute when clicked.
         */
        private Command command;

        /** Should clicking this menu item hide the popup? */
        private final boolean hide;

        /**
         * Constructs a {@PopupMenuItem}
         *
         * @param text The text label for the item.
         * @param cmd The command to run when the item is clicked.
         * @param isEnabled True if this menu item is enabled.
         * @param hide True if clicking this menu item should hide the popup.
         */
        public PopupMenuItem(String text, Command cmd, boolean isEnabled, boolean hide) {
            super(text);
            setStyleName(RESOURCES.css().item());
            setEnabled(isEnabled);
            defaultEnabled = isEnabled;
            command = cmd;
            this.hide = hide;
            if (isPreClicked) {
                // If this menu is pre-clicked it doesn't require a full click to select
                // an item, just a mouseup over the item.  If the user then does click the
                // item then that will also give a mouseup so this handler will deal with
                // that case as well.
                addMouseUpHandler(new MouseUpHandler() {
                    @Override
                    public void onMouseUp(MouseUpEvent event) {
                        onClicked();
                    }
                });
            } else {
                addClickHandler(new ClickHandler() {
                    public void onClick(ClickEvent e) {
                        onClicked();
                    }
                });
            }
            // Ensure that clicking this menu item doesn't affect the current selection.
            addMouseDownHandler(PREVENT_DEFAULT_HANDLER);
        }

        private void onClicked() {
            if (enabled) {
                if (command != null) {
                    command.execute();
                }
                if (hide) {
                    popup.hide();
                }
            }
        }

        @Override
        public void setEnabled(boolean enabled) {
            if (!enabled) {
                addStyleName(RESOURCES.css().disabled());
            } else {
                removeStyleName(RESOURCES.css().disabled());
            }
            this.enabled = enabled;
        }

        @Override
        public void resetEnabled() {
            setEnabled(defaultEnabled);
        }

        @Override
        public final void setDebugClassTODORename(String debugClass) {
            DebugClassHelper.addDebugClass(this, debugClass);
        }

        /** Set the command to me executed when this menu item is clicked */
        public void setCommand(Command cmd) {
            command = cmd;
        }
    }

    /**
     * Mouse down handler that prevents the event's default action thereby, the
     * way it's used here, preventing mouse downs from affecting selection.
     */
    private static final MouseDownHandler PREVENT_DEFAULT_HANDLER = new MouseDownHandler() {
        @Override
        public void onMouseDown(MouseDownEvent event) {
            event.preventDefault();
        }
    };

    /** The singleton instance of our resources. */
    private static final Resources RESOURCES = GWT.create(Resources.class);
    public static final Resources.Css CSS = RESOURCES.css();

    /**
     * Inject the CSS once.
     */
    static {
        StyleInjector.inject(RESOURCES.css().getText());
    }

    /** The popup to use for this submenu */
    private final UniversalPopup popup;

    /**
     * True if the user has already clicked a mouse button and so just releasing the
     * mouse over an item should count as a click.
     */
    private final boolean isPreClicked;

    /**
     * Create a new PopupMenu
     *
     * @param relative The reference element for the positioner.
     * @param positioner The positioner to position the menu popup.
     */
    public PopupMenu(Element relative, RelativePopupPositioner positioner, boolean isPreClicked) {
        popup = PopupFactory.createPopup(relative, positioner, PopupChromeFactory.createPopupChrome(), true);
        this.isPreClicked = isPreClicked;
    }

    /**
     * Create a new PopupMenu
     *
     * @param relative The reference element for the positioner.
     * @param positioner The positioner to position the menu popup.
     */
    public PopupMenu(Element relative, RelativePopupPositioner positioner) {
        this(relative, positioner, false);
    }

    @Override
    public void addDivider() {
        Widget divider = new Label();
        divider.setStyleName(RESOURCES.css().divider());
        popup.add(divider);
    }

    @Override
    public Pair<Menu, MenuItem> createSubMenu(String title, boolean enabled) {
        PopupMenuItem item = addItem(title, null, enabled, false);
        final PopupMenu menu = new PopupMenu(item.getElement(), AlignedPopupPositioner.BELOW_RIGHT);
        item.setCommand(new Command() {
            @Override
            public void execute() {
                menu.show();
            }
        });
        return new Pair<Menu, MenuItem>(menu, item);
    }

    @Override
    public MenuItem addItem(String label, final Command cmd, boolean enabled) {
        return addItem(label, cmd, enabled, true);
    }

    private PopupMenuItem addItem(String label, final Command cmd, boolean enabled, boolean hideOnClick) {
        PopupMenuItem item = new PopupMenuItem(label, cmd, enabled, hideOnClick);
        popup.add(item);
        return item;
    }

    /**
     * Removes an item previously added to the menu.
     *
     * @param item The item to remove.
     */
    public boolean removeItem(MenuItem item) {
        if (item instanceof PopupMenuItem) {
            PopupMenuItem popup = (PopupMenuItem) item;
            return this.popup.remove(popup);
        }
        return false;
    }

    /**
     * Hide the menu.
     */
    public void hide() {
        popup.hide();
    }

    /**
     * Show the menu.
     */
    public void show() {
        popup.show();
    }

    /**
     * Set the debugClassName (for Webdriver) on the popup window.
     * @param dcName debugClassName to set.
     */
    public void setDebugClass(String dcName) {
        popup.setDebugClass(dcName);
    }

    @Override
    public void clearItems() {
        popup.clear();
    }

    /**
     * Add a PopupEventListener to this menu.
     * @param listener The listener to add.
     */
    public void addPopupEventListener(PopupEventListener listener) {
        popup.addPopupEventListener(listener);
    }

    /**
     * Remove a PopupEventListener to this menu.
     * @param listener The listener to remove.
     */
    public void removePopupEventListener(PopupEventListener listener) {
        popup.removePopupEventListener(listener);
    }

    /**
     * Indicates that clicks inside the given widget should not be considered
     * 'outside' of the popup menu, for purposes of auto-hiding.
     *
     * @param w A widget.
     */
    public void associateWidget(Widget w) {
        popup.associateWidget(w);
    }
}