package org.geogebra.web.web.gui.toolbar;
import java.util.ArrayList;
import java.util.Vector;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.ModeSetter;
import org.geogebra.common.util.debug.Log;
import org.geogebra.web.html5.euclidian.IsEuclidianController;
import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW;
import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW.ToolTipLinkType;
import org.geogebra.web.html5.gui.util.CancelEventTimer;
import org.geogebra.web.html5.gui.util.ListItem;
import org.geogebra.web.html5.gui.util.NoDragImage;
import org.geogebra.web.html5.gui.util.UnorderedList;
import org.geogebra.web.html5.main.AppW;
import org.geogebra.web.web.gui.app.GGWToolBar;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.HumanInputEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
public class ModeToggleMenuW extends ListItem implements MouseDownHandler, MouseUpHandler,
TouchStartHandler, TouchEndHandler, MouseOutHandler, MouseOverHandler, KeyUpHandler{
protected FlowPanel tbutton;
protected ToolbarSubmenuW submenu;
protected AppW app;
protected ToolBarW toolbar;
protected final Vector<Integer> menu;
private boolean wasMenuShownOnMouseDown;
protected int order;
public ModeToggleMenuW(AppW appl, Vector<Integer> menu1, ToolBarW tb, int order) {
super();
this.order = order;
this.app = appl;
this.toolbar = tb;
this.menu = menu1;
this.addStyleName("toolbar_item");
buildButton();
}
protected void buildButton() {
tbutton = new FlowPanel();
tbutton.addStyleName("toolbar_button");
Image toolbarImg = new NoDragImage(((GGWToolBar)app.getToolbar()).getImageURL(menu.get(0).intValue()),32);
toolbarImg.addStyleName("toolbar_icon");
tbutton.add(toolbarImg);
tbutton.getElement().setAttribute("mode",menu.get(0).intValue()+"");
tbutton.getElement().setAttribute("isMobile", "false");
addDomHandlers(tbutton);
addPasteHandlerTo(tbutton.getElement());
this.add(tbutton);
}
public native void addPasteHandlerTo(Element elem) /*-{
var self = this;
var wind = $wnd;
var docu = $doc;
elem.onkeydown = function(event) {
event.stopPropagation();
};
elem.onkeypress = function(event) {
event.stopPropagation();
};
if (wind.onpastehandleradded === undefined) {
// onpastehandler has nothing to do with
// "elem", so it's enough to register it once
// but remember whether it's registered
wind.onpastehandleradded = function(event) {
event.stopPropagation();
var cbd;
if (event.clipboardData) {
// all the other browsers
cbd = event.clipboardData;
} else if (wind.clipboardData) {
// Windows Internet Explorer
cbd = wind.clipboardData;
}
if (cbd.items) {
var is = cbd.items;
for (var cv = 0; cv < is.length; cv++) {
if (is[cv].type.indexOf('image') > -1) {
var bb = is[cv].getAsFile();
var ur = wind.URL || wind.webkitURL;
var sr = ur.createObjectURL(bb);
var pi = new Image();
pi.onload = function() {
// some code to get the data URL
// which GeoGebra knows how to convert
var ca = docu.createElement('canvas');
ca.width = pi.width;
ca.height = pi.height;
var ct = ca.getContext("2d");
ct.drawImage(pi, 0, 0);
var dl = ca.toDataURL("image/png");
self.@org.geogebra.web.web.gui.toolbar.ModeToggleMenuW::onPaste(Ljava/lang/String;)(dl);
}
pi.src = sr;
// do not paste more images at once!
break;
}
}
}
return false;
};
// this seems to run in Chrome!
wind.addEventListener("paste", wind.onpastehandleradded);
}
}-*/;
public void onPaste(String str) {
app.getGgbApi().insertImage(str);
}
protected void buildGui() {
submenu = createToolbarSubmenu(app, order);
add(submenu);
for (int k = 0; k < menu.size(); k++) {
final int addMode = menu.get(k).intValue();
if (addMode < 0) { // TODO
// // separator within menu:
// tm.addSeparator();
} else { // standard case: add mode
// check mode
if (!"".equals(app.getToolName(addMode))) {
ListItem subLi = submenu.addItem(addMode);
addDomHandlers(subLi);
}
}
}
hideMenu();
}
protected ToolbarSubmenuW createToolbarSubmenu(AppW app, int order) {
return new ToolbarSubmenuW(app, order);
}
public void setButtonTabIndex(int index){
tbutton.getElement().setTabIndex(index);
}
public UnorderedList getItemList(){
if (submenu != null) {
return submenu.getItemList();
}
return null;
}
public void addDomHandlers(Widget w){
w.addDomHandler(this, MouseDownEvent.getType());
w.addDomHandler(this, MouseUpEvent.getType());
w.addDomHandler(this, TouchStartEvent.getType());
w.addDomHandler(this, TouchEndEvent.getType());
w.addDomHandler(this, MouseOverEvent.getType());
w.addDomHandler(this, MouseOutEvent.getType());
w.addDomHandler(this, KeyUpEvent.getType());
}
/**
* Sets the menu visible if it exists
*/
public void showMenu() {
if(this.submenu == null){
this.buildGui();
}
if (submenu != null) {
submenu.setVisible(true);
app.registerPopup(submenu);
}
}
/**
* Hides the menu if it exists
*/
public void hideMenu() {
if (submenu != null) {
app.unregisterPopup(submenu);
submenu.setVisible(false);
}
}
/**
* @param visible if true sets the menu visible, otherwise it hides it
*/
public void setMenuVisibility(boolean visible) {
if (submenu == null) {
return;
}
if (visible) {
showMenu();
} else {
hideMenu();
}
}
public boolean selectMode(int mode, ModeSetter m) {
String modeText = mode + "";
boolean imageDialog = mode == EuclidianConstants.MODE_IMAGE;
//If there is only one menuitem, there is no submenu -> set the button selected, if the mode is the same.
if (menu.size() == 1 && !imageDialog) {
if (menu.get(0) == mode) {
showToolTipBottom(mode, m);
this.setCssToSelected();
toolbar.update(); //TODO! needed to regenerate the toolbar, if we want to see the border.
//remove, if it will be updated without this.
return true;
}
return false;
}
boolean needsGUI = false;
if (getItemList() == null) {
if (menu.get(0) == mode){
this.setCssToSelected();
toolbar.update(); //TODO! needed to regenerate the toolbar, if we want to see the border.
//remove, if it will be updated without this.
return true;
}
for(Integer i: this.menu){
if(i == mode){
needsGUI = true;
}
}
if (!needsGUI) {
return false;
}
}
if (needsGUI) {
buildGui();
}
for (int i = 0; i < getItemList().getWidgetCount(); i++) {
Widget mi = getItemList().getWidget(i); // submenuitems
// found item for mode?
if (mi.getElement().getAttribute("mode").equals(modeText)) {
if (!imageDialog) {
selectItem(mi);
}
showToolTipBottom(mode, m);
return true;
}
}
// tbutton.getElement().setAttribute("isSelected", "false");
return false;
}
public int getFirstMode() {
if (menu.size() == 0){
return -1;
}
int firstmode = menu.get(0);
return firstmode;
}
void selectItem(Widget mi) {
final String miMode = mi.getElement().getAttribute("mode");
// check if the menu item is already selected
if (tbutton.getElement().getAttribute("isSelected").equals("true")
&& tbutton.getElement().getAttribute("mode").equals(miMode)) {
return;
}
tbutton.getElement().setAttribute("mode",miMode);
//
tbutton.clear();
Image buttonImage = new NoDragImage(((GGWToolBar) app.getToolbar())
.getImageURL(Integer.parseInt(miMode)), 32);
buttonImage.addStyleName("toolbar_icon");
tbutton.add(buttonImage);
toolbar.update();
setCssToSelected();
}
private void setCssToSelected(){
ArrayList<ModeToggleMenuW> modeToggleMenus = toolbar.getModeToggleMenus();
for (int i = 0; i < modeToggleMenus.size(); i++) {
ModeToggleMenuW mtm = modeToggleMenus.get(i);
if (mtm != this) {
mtm.getToolbarButtonPanel().getElement().getStyle().setBorderWidth(1, Unit.PX);
mtm.getToolbarButtonPanel().getElement().setAttribute("isSelected","false");
}
}
//Set border width explicitly to make sure browser actually does that
// (otherwise the thicker border applies on next browser event)
getToolbarButtonPanel().getElement().setAttribute("isSelected","true");
getToolbarButtonPanel().getElement().getStyle().setBorderWidth(2, Unit.PX);
}
public void addSeparator(){
//TODO
}
public void onEnd(DomEvent<?> event) {
int mode = Integer.parseInt(event.getRelativeElement().getAttribute(
"mode"));
if (mode < 999 || mode > 2000) {
app.hideKeyboard();
}
tbutton.getElement().focus();
event.stopPropagation();
if (event.getSource() == tbutton) { // if click ended on the button
// if enter was pressed
if ((event instanceof KeyUpEvent) && ((KeyUpEvent)event).getNativeKeyCode() == KeyCodes.KEY_ENTER){
setMenuVisibility(!isMenuShown());
}
// if submenu was open
if (wasMenuShownOnMouseDown && !(event instanceof TouchEndEvent && app.getLAF().isSmart())) {
hideMenu();
}
} else { // click ended on menu item
hideMenu();
event.stopPropagation();
}
ToolTipManagerW.sharedInstance().setBlockToolTip(false);
//if we click the toolbar button, only interpret it as real click if there is only one tool in this menu
app.setMode(
mode,
event.getSource() == tbutton && menu.size() > 1 ? ModeSetter.DOCK_PANEL : ModeSetter.TOOLBAR);
ToolTipManagerW.sharedInstance().setBlockToolTip(true);
tbutton.getElement().focus();
}
@Override
public void onTouchStart(TouchStartEvent event) {
if (event.getSource() == tbutton) {
onStart(event);
CancelEventTimer.touchEventOccured();
} else { // clicked on a submenu list item
event.stopPropagation(); // the submenu doesn't close as a popup, see GeoGebraAppFrame init()
}
event.preventDefault();
}
@Override
public void onTouchEnd(TouchEndEvent event) {
onEnd(event);
CancelEventTimer.touchEventOccured();
}
@Override
public void onMouseUp(MouseUpEvent event) {
if(CancelEventTimer.cancelMouseEvent()){
return;
}
onEnd(event);
if (event.getSource() == tbutton) {
if (this.app.getActiveEuclidianView() != null
&& this.app.getActiveEuclidianView()
.getEuclidianController() != null) {
((IsEuclidianController) this.app.getActiveEuclidianView()
.getEuclidianController())
.setActualSticky(
event.getNativeButton() == NativeEvent.BUTTON_RIGHT);
}
}
}
@Override
public void onMouseDown(MouseDownEvent event) {
if (event.getSource() == tbutton
&& !CancelEventTimer.cancelMouseEvent()) {
onStart(event);
} else { // clicked on a submenu list item
showTooltipFor(event);
event.stopPropagation(); // the submenu doesn't close as a popup, see GeoGebraAppFrame init()
}
event.preventDefault();
}
/**
* Handles the touchstart and mousedown events on main tools.
*
* @param event
* mouse or touch event
*/
public void onStart(HumanInputEvent<?> event) {
event.preventDefault();
event.stopPropagation();
this.setFocus(true);
if (isMenuShown()) {
wasMenuShownOnMouseDown = true;
} else {
toolbar.closeAllSubmenu();
wasMenuShownOnMouseDown = false;
showMenu();
if (menu.size() == 1) {
showTooltipFor(event);
}
}
}
protected void showTooltipFor(HumanInputEvent<?> event) {
ToolTipManagerW.sharedInstance().setBlockToolTip(false);
int mode = -1;
if (event.getSource() == tbutton) {
mode = menu.get(0);
} else {
mode = Integer.parseInt(event.getRelativeElement().getAttribute(
"mode"));
}
if (mode >= 0) {
// if we click the toolbar button, only interpret it as real
// click if there is only one tool in this menu
showToolTipBottom(mode, ModeSetter.TOOLBAR);
}
ToolTipManagerW.sharedInstance().setBlockToolTip(true);
}
public void showToolTipBottom(int mode, ModeSetter m) {
if (m != ModeSetter.CAS_VIEW
&& app.showToolBarHelp()) {
ToolTipManagerW.sharedInstance().showBottomInfoToolTip(
app.getToolTooltipHTML(mode),
app.getGuiManager().getTooltipURL(mode),
ToolTipLinkType.Help, app,
app.getAppletFrame().isKeyboardShowing());
}
}
/*
public void hideToolTip(){
ToolTipManagerW.sharedInstance().hideToolTip();
}*/
/**
* @return true if the menu is open
*/
public boolean isMenuShown() {
if (submenu != null) {
return submenu.isVisible();
}
return false;
}
@Override
public void onMouseOver(MouseOverEvent event) {
if (event.getSource() != tbutton) {
setHovered(event.getRelativeElement(), true);
showTooltipFor(event);
return;
}
if (!isMenuShown() && toolbar.isAnyOtherSubmenuOpen(this)) {
toolbar.closeAllSubmenu();
showMenu();
}
}
@Override
public void onMouseOut(MouseOutEvent event) {
// Avoid opening submenu, if a user presses a button for a while,
// then move on an another button without mouseup.
if(event.getSource() == tbutton){
return;
}
//submenu's menuitem won't be highlighted
setHovered(event.getRelativeElement(), false);
}
private static void setHovered(Element el, boolean hovered) {
if (hovered){
el.addClassName("hovered");
} else {
el.removeClassName("hovered");
}
}
@Override
public void onKeyUp(KeyUpEvent event) {
int keyCode = event.getNativeKeyCode();
switch (keyCode){
default:
// do nothing
break;
case KeyCodes.KEY_ENTER:
onEnd(event);
break;
case KeyCodes.KEY_RIGHT:
case KeyCodes.KEY_LEFT:
int indexOfButton = toolbar.getModeToggleMenus().indexOf(this);
if (keyCode == KeyCodes.KEY_RIGHT){
indexOfButton++;
} else {
indexOfButton--;
}
if (indexOfButton >= 0 && indexOfButton < toolbar.getModeToggleMenus().size()){
selectMenu(indexOfButton);
}else{
toolbar.selectMenuButton(indexOfButton < 0 ? -1 : 0);
}
break;
case KeyCodes.KEY_DOWN:
if (event.getSource() == tbutton){
if(isMenuShown()){
this.getItemList().getWidget(0).getElement().focus();
} else {
showMenu();
this.getItemList().getWidget(0).getElement().focus();
}
} else {
Element nextSiblingElement = event.getRelativeElement().getNextSiblingElement();
if (nextSiblingElement != null){
nextSiblingElement.focus();
} else {
event.getRelativeElement().getParentElement().getFirstChildElement().focus();
}
}
break;
case KeyCodes.KEY_UP:
if (event.getSource() instanceof ListItem){
Element previousSiblingElement = event.getRelativeElement().getPreviousSiblingElement();
if (previousSiblingElement != null){
previousSiblingElement.focus();
} else {
UnorderedList parentUL = (UnorderedList)((ListItem)(event.getSource())).getParent();
parentUL.getWidget(parentUL.getWidgetCount()-1).getElement().focus();
}
}
break;
}
}
private void selectMenu(int index){
ModeToggleMenuW mtm2 = toolbar.getModeToggleMenus().get(index);
mtm2.tbutton.getElement().focus();
if(isMenuShown()){
hideMenu();
mtm2.showMenu();
}
}
/**
* @return the panel containing the toolbar button
*/
public FlowPanel getToolbarButtonPanel() {
return tbutton;
}
public void addModes(Vector<Integer> menu2) {
if(this.submenu == null){
this.buildGui();
}
for (int k = 0; k < menu2.size(); k++) {
final int addMode = menu2.get(k).intValue();
if (addMode < 0) { // TODO
// // separator within menu:
// tm.addSeparator();
} else { // standard case: add mode
// check mode
if (app.isModeValid(addMode)) {
ListItem subLi = submenu.addItem(addMode);
addDomHandlers(subLi);
} else {
Log.debug("Invalid toolbar mode: " + addMode);
}
}
}
}
public void setMaxHeight(double d) {
if(submenu != null){
this.submenu.setMaxHeight((int)d);
}
}
}