/** * Copyright 2010 Google Inc. * * Licensed 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.toolbar; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.StyleInjector; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; import org.waveprotocol.wave.client.widget.popup.UniversalPopup; import org.waveprotocol.wave.client.widget.toolbar.buttons.AbstractToolbarButton; import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarButtonView; import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarClickButton; import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarPopupToggler; import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarToggleButton; import org.waveprotocol.wave.client.widget.toolbar.buttons.VerticalToolbarButtonWidget; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.IdentitySet; import java.util.List; /** * A toolbar within a submenu. * * @author kalman@google.com (Benjamin Kalman) */ public final class SubmenuToolbarWidget extends AbstractToolbarButton implements SubmenuToolbarView, GroupingToolbar.View, SubmenuItem.Parent, ToolbarPopupToggler.Listener { interface Resources extends ClientBundle { @Source("SubmenuToolbarWidget.css") Css css(); } interface Css extends CssResource { String toolbar(); } static final Resources res = GWT.create(Resources.class); static { StyleInjector.inject(res.css().getText(), true); } /** All items in the toolbar. */ private final List<ToolbarButtonView> items = CollectionUtils.newArrayList(); /** The set of child items which are enabled. */ private final IdentitySet<SubmenuItem> enabledChildren = CollectionUtils.createIdentitySet(); /** The contents of the popup. */ private final FlowPanel panel = new FlowPanel(); /** The active popup, maintained so that button actions can hide it. */ private UniversalPopup activePopup = null; /** Listener for the submenu being shown/hidden. */ private Listener listener = null; SubmenuToolbarWidget(ToolbarToggleButton button) { // NOTE: don't let AbstractToolbarButton add a click listener for the // button, this would override the existing click listener added for the // toggle button. super(button.getButton(), false); ToolbarPopupToggler.associate(button, this); } @Override public void setListener(Listener listener) { this.listener = listener; } @Override public ToolbarClickButton addClickButton() { ToolbarClickButton button = insertClickButton(items.size()); showDividerIfNotFirst(button); return button; } @Override public ToolbarToggleButton addToggleButton() { ToolbarToggleButton button = insertToggleButton(items.size()); showDividerIfNotFirst(button); return button; } @Override public SubmenuToolbarWidget addSubmenu() { SubmenuToolbarWidget submenu = insertSubmenu(items.size()); showDividerIfNotFirst(submenu); return submenu; } private void showDividerIfNotFirst(ToolbarButtonView button) { if (button != items.get(0)) { button.setShowDivider(true); } } @Override public ToolbarView addGroup() { return new GroupingToolbar(this, addFakeItem()); } /** * Adds a fake item that isn't rendered but still has an entry in items. */ private ToolbarButtonView addFakeItem() { // NOTE: simplest way to add a fake item is to add an invisible button. ToolbarButtonView fakeButton = addClickButton(); fakeButton.setState(State.INVISIBLE); return fakeButton; } @Override public void clearItems() { panel.clear(); enabledChildren.clear(); items.clear(); setState(State.DISABLED); } // // For GroupingToolbar.View // @Override public ToolbarClickButton insertClickButton(int beforeIndex) { ToolbarClickButton button = new ToolbarClickButton(new VerticalToolbarButtonWidget()); insertAbstractButton(button, beforeIndex); return button; } @Override public ToolbarToggleButton insertToggleButton(int beforeIndex) { ToolbarToggleButton button = new ToolbarToggleButton(new VerticalToolbarButtonWidget()); insertAbstractButton(button, beforeIndex); return button; } private void insertAbstractButton(AbstractToolbarButton button, int beforeIndex) { button.setParent(this); panel.insert(button.hackGetWidget(), beforeIndex); items.add(beforeIndex, button); enabledChildren.add(button); setState(State.ENABLED); } @Override public SubmenuToolbarWidget insertSubmenu(int beforeIndex) { ToolbarToggleButton submenuButton = new ToolbarToggleButton(new VerticalToolbarButtonWidget()); submenuButton.setShowDropdownArrow(true); panel.insert(submenuButton.hackGetWidget(), beforeIndex); SubmenuToolbarWidget submenu = new SubmenuToolbarWidget(submenuButton); // NOTE: don't set action listener or parent on the submenu button, set on // the submenu itself. submenu.setParent(this); items.add(beforeIndex, submenu); return submenu; } @Override public int indexOf(ToolbarButtonView button) { return items.indexOf(button); } /** * Directly inserts a widget. Package-private for the overflow submenu. */ void hackInsertWidget(Widget w, int beforeIndex) { panel.insert(w, beforeIndex); } // // Popup related // @Override public void onPopupCreated(UniversalPopup popup) { popup.add(panel); activePopup = popup; if (listener != null) { listener.onSubmenuShown(); } } @Override public void onPopupDestroyed(UniversalPopup popup) { activePopup = null; if (listener != null) { listener.onSubmenuHidden(); } } // // SubmenuItem.Parent // @Override public void onActionPerformed() { if (activePopup != null) { activePopup.hide(); if (getParent() != null) { getParent().onActionPerformed(); } } } @Override public void onChildStateChanged(SubmenuItem item, State newState) { if (newState == State.ENABLED) { enabledChildren.add(item); setState(State.ENABLED); } else { enabledChildren.remove(item); setState(enabledChildren.isEmpty() ? State.DISABLED : State.ENABLED); } } }