/*
* Toolbar.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.core.client.widget;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
import org.rstudio.core.client.BrowseCap;
import org.rstudio.core.client.SeparatorManager;
import org.rstudio.core.client.dom.DomUtils;
import org.rstudio.core.client.dom.DomUtils.ElementPredicate;
import org.rstudio.core.client.resources.ImageResource2x;
import org.rstudio.core.client.theme.res.ThemeResources;
import org.rstudio.core.client.theme.res.ThemeStyles;
import java.util.AbstractList;
public class Toolbar extends Composite
{
private static class ChildWidgetList extends AbstractList<Widget>
{
private ChildWidgetList(ComplexPanel panel)
{
panel_ = panel;
}
@Override
public Widget get(int index)
{
return panel_.getWidget(index);
}
@Override
public int size()
{
return panel_.getWidgetCount();
}
private ComplexPanel panel_;
}
private class ToolbarSeparatorManager extends SeparatorManager<Widget>
{
@Override
protected boolean isSeparator(Widget item)
{
return styles_.toolbarSeparator().equals(item.getStylePrimaryName());
}
@Override
protected boolean isVisible(Widget item)
{
return item.isVisible();
}
@Override
protected void setVisible(Widget item, boolean visible)
{
item.setVisible(visible);
}
}
public Toolbar(Widget[] leftWidgets, Widget[] rightWidgets)
{
this();
if (leftWidgets != null)
{
for (int i = 0; i < leftWidgets.length; i++)
{
if (i > 0)
addLeftSeparator();
addLeftWidget(leftWidgets[i]);
}
}
if (rightWidgets != null)
{
for (int i = 0; i < rightWidgets.length; i++)
{
if (i > 0)
addRightSeparator();
addRightWidget(rightWidgets[i]);
}
}
}
public Toolbar()
{
super();
toolbarWrapper_ = new HTMLPanel("");
horizontalPanel_ = new HorizontalPanel();
horizontalPanel_.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
leftToolbarPanel_ = new HorizontalPanel();
leftToolbarPanel_.setVerticalAlignment(
HasVerticalAlignment.ALIGN_MIDDLE);
horizontalPanel_.add(leftToolbarPanel_);
horizontalPanel_.setCellHorizontalAlignment(
leftToolbarPanel_,
HasHorizontalAlignment.ALIGN_LEFT);
rightToolbarPanel_ = new HorizontalPanel();
rightToolbarPanel_.setVerticalAlignment(
HasVerticalAlignment.ALIGN_MIDDLE);
horizontalPanel_.add(rightToolbarPanel_);
horizontalPanel_.setCellHorizontalAlignment(
rightToolbarPanel_,
HasHorizontalAlignment.ALIGN_RIGHT);
horizontalPanel_.addStyleName(styles_.toolbar());
toolbarWrapper_.add(horizontalPanel_);
initWidget(toolbarWrapper_);
setStyleName(styles_.toolbarWrapper());
}
protected void manageSeparators()
{
separatorsInvalidated_ = false;
new ToolbarSeparatorManager().manageSeparators(
new ChildWidgetList(leftToolbarPanel_));
new ToolbarSeparatorManager().manageSeparators(
new ChildWidgetList(rightToolbarPanel_));
updateStyles(leftToolbarPanel_);
updateStyles(rightToolbarPanel_);
}
public void invalidateSeparators()
{
if (!separatorsInvalidated_)
{
separatorsInvalidated_ = true;
Scheduler.get().scheduleFinally(new ScheduledCommand()
{
public void execute()
{
manageSeparators();
}
});
}
}
private void updateStyles(HorizontalPanel panel)
{
if (BrowseCap.isSafari())
updateStylesSafari(panel);
}
// This is used to work around a button sizing issue on Safari where
// TD elements with a child element having 'display: none' might
// still take up some space, thereby messing with the layout
// when the DOM has a number of TD elements with undisplayed
// contents.
private void updateStylesSafari(HorizontalPanel panel)
{
for (int i = 0; i < panel.getWidgetCount(); i++)
{
Widget widget = panel.getWidget(i);
boolean visible = widget.isVisible();
DomUtils.toggleParentVisibility(widget.getElement(), visible, new ElementPredicate()
{
@Override
public boolean test(Element el)
{
return el.getTagName().toLowerCase().equals("td");
}
});
}
}
public <TWidget extends Widget> TWidget addLeftWidget(TWidget widget)
{
leftToolbarPanel_.add(widget);
invalidateSeparators();
return widget;
}
public <TWidget extends Widget> TWidget addLeftWidget(
TWidget widget,
VerticalAlignmentConstant alignment)
{
addLeftWidget(widget);
leftToolbarPanel_.setCellVerticalAlignment(widget, alignment);
invalidateSeparators();
return widget;
}
public interface MenuSource
{
ToolbarPopupMenu getMenu();
}
public Widget addLeftPopupMenu(Label label, final ToolbarPopupMenu menu)
{
return addToolbarPopupMenu(new SimpleMenuLabel(label), menu, true);
}
public Widget addLeftPopupMenu(MenuLabel label, final ToolbarPopupMenu menu)
{
return addToolbarPopupMenu(label, menu, true);
}
public Widget addRightPopupMenu(MenuLabel label, final ToolbarPopupMenu menu)
{
return addToolbarPopupMenu(label, menu, false);
}
public static Widget getSeparator()
{
Image sep = new Image(ThemeResources.INSTANCE.toolbarSeparator());
return sep;
}
public Widget addLeftSeparator()
{
Image sep = new ToolbarSeparator();
leftToolbarPanel_.add(sep);
invalidateSeparators();
return sep;
}
public Widget addRightSeparator()
{
Image sep = new ToolbarSeparator();
rightToolbarPanel_.add(sep);
invalidateSeparators();
return sep;
}
public <TWidget extends Widget> TWidget addRightWidget(TWidget widget)
{
rightToolbarPanel_.add(widget);
invalidateSeparators();
return widget;
}
public void removeLeftWidget(Widget widget)
{
leftToolbarPanel_.remove(widget);
}
public void removeLeftWidgets()
{
removeAllWidgets(leftToolbarPanel_);
}
public void removeRightWidget(Widget widget)
{
rightToolbarPanel_.remove(widget);
}
public void removeRightWidgets()
{
removeAllWidgets(rightToolbarPanel_);
}
public void removeAllWidgets()
{
removeLeftWidgets();
removeRightWidgets();
}
public int getHeight()
{
int offsetHeight = getOffsetHeight();
if (offsetHeight != 0)
return offsetHeight;
else
return DEFAULT_HEIGHT;
}
@Override
public void addStyleName(String styleName)
{
horizontalPanel_.addStyleName(styleName);
}
public Widget getWrapper()
{
return toolbarWrapper_;
}
private void removeAllWidgets(HorizontalPanel panel)
{
for (int i = panel.getWidgetCount()-1; i >= 0; i--)
panel.remove(i);
}
private Widget addToolbarPopupMenu(
MenuLabel label,
final ToolbarPopupMenu menu,
boolean left)
{
return addPopupMenu(label, new MenuSource() {
@Override
public ToolbarPopupMenu getMenu()
{
return menu;
}
}, left);
}
private Widget addPopupMenu(final MenuLabel menuLabel,
final MenuSource menuSource,
boolean left)
{
final Widget label = menuLabel.asWidget();
label.setStylePrimaryName("rstudio-StrongLabel") ;
label.getElement().getStyle().setCursor(Style.Cursor.DEFAULT);
label.getElement().getStyle().setOverflow(Overflow.HIDDEN);
if (left)
addLeftWidget(label);
else
addRightWidget(label);
Image image = new Image(new ImageResource2x(ThemeResources.INSTANCE.menuDownArrow2x()));
image.getElement().getStyle().setMarginLeft(5, Unit.PX);
image.getElement().getStyle().setMarginRight(8, Unit.PX);
image.getElement().getStyle().setMarginBottom(2, Unit.PX);
if (left)
addLeftWidget(image);
else
addRightWidget(image);
final ClickHandler clickHandler = new ClickHandler()
{
public void onClick(ClickEvent event)
{
ToolbarPopupMenu menu = menuSource.getMenu();
menu.showRelativeTo(label);
menu.getElement().getStyle().setPaddingTop(3, Style.Unit.PX);
}
};
menuLabel.addClickHandler(clickHandler);
image.addClickHandler(clickHandler);
return image;
}
private HorizontalPanel horizontalPanel_ ;
private HorizontalPanel leftToolbarPanel_ ;
private HorizontalPanel rightToolbarPanel_ ;
private HTMLPanel toolbarWrapper_;
protected final ThemeStyles styles_ = ThemeResources.INSTANCE.themeStyles();
private boolean separatorsInvalidated_ = false;
public static final int DEFAULT_HEIGHT = 22;
}