package org.geogebra.web.html5.euclidian;
import org.geogebra.common.awt.GBasicStroke;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GDimension;
import org.geogebra.common.awt.GFont;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.euclidian.EuclidianController;
import org.geogebra.common.euclidian.EuclidianCursor;
import org.geogebra.common.euclidian.EuclidianStyleBar;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.euclidian.MyZoomer;
import org.geogebra.common.euclidian.event.PointerEventType;
import org.geogebra.common.io.MyXMLio;
import org.geogebra.common.javax.swing.GBox;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.main.App;
import org.geogebra.common.main.App.ExportType;
import org.geogebra.common.main.Feature;
import org.geogebra.common.main.settings.EuclidianSettings;
import org.geogebra.common.plugin.EuclidianStyleConstants;
import org.geogebra.common.util.debug.GeoGebraProfiler;
import org.geogebra.common.util.debug.Log;
import org.geogebra.web.html5.Browser;
import org.geogebra.web.html5.awt.GBasicStrokeW;
import org.geogebra.web.html5.awt.GDimensionW;
import org.geogebra.web.html5.awt.GFontW;
import org.geogebra.web.html5.awt.GGraphics2DW;
import org.geogebra.web.html5.awt.PrintableW;
import org.geogebra.web.html5.gawt.GBufferedImageW;
import org.geogebra.web.html5.gui.GeoGebraFrameW;
import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW;
import org.geogebra.web.html5.gui.util.CancelEventTimer;
import org.geogebra.web.html5.gui.util.ClickStartHandler;
import org.geogebra.web.html5.javax.swing.GBoxW;
import org.geogebra.web.html5.main.AppW;
import org.geogebra.web.html5.main.TimerSystemW;
import org.geogebra.web.html5.util.Dom;
import org.geogebra.web.html5.util.ImageLoadCallback;
import org.geogebra.web.html5.util.ImageWrapper;
import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.DropEvent;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.GestureChangeEvent;
import com.google.gwt.event.dom.client.GestureEndEvent;
import com.google.gwt.event.dom.client.GestureStartEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class EuclidianViewW extends EuclidianView implements
EuclidianViewWInterface, PrintableW {
final public static int DELAY_BETWEEN_MOVE_EVENTS = 30;
public GGraphics2DW g2p = null;
private GGraphics2D g2dtemp;
public GGraphics2DW g4copy = null;
private GColor backgroundColor = GColor.WHITE;
private AnimationScheduler.AnimationCallback repaintCallback = new AnimationScheduler.AnimationCallback() {
@Override
public void execute(double ts) {
doRepaint2();
}
};
private AnimationScheduler repaintScheduler = AnimationScheduler.get();
private long lastRepaint;
public boolean isInFocus = false;
AppW app = (AppW) super.app;
protected ImageElement resetImage, playImage, pauseImage, upArrowImage,
downArrowImage, playImageHL, pauseImageHL;
protected EuclidianPanelWAbstract EVPanel;
private PointerEventHandler pointerHandler;
// firstInstance is necessary for proper cycling
private static EuclidianViewWInterface firstInstance = null;
// lastInstance is necessary for knowing when to cycle
private static EuclidianViewWInterface lastInstance = null;
// tells whether recently TAB is pressed in some Graphics View
// in some applet, which SHOULD move focus to another Graphics View
// of another (or the same) applet... when this happens, the
// focus handler of the target applet runs, and sets this static
// variable false again, so this is just a technical solution
// for deciding, whether to select the first GeoElement in that
// applet or not (because we shall not change selection in case
// e.g. the spreadsheet view gives focus to Graphics view).
private static boolean tabPressed = false;
/**
* @param euclidianViewPanel
* @param euclidiancontroller
* @param evNo
* @param settings
*/
public EuclidianViewW(EuclidianPanelWAbstract euclidianViewPanel,
EuclidianController euclidiancontroller, int evNo,
EuclidianSettings settings) {
super(euclidiancontroller, evNo, settings);
viewTextField = new ViewTextFieldW(this);
EVPanel = euclidianViewPanel;
initBaseComponents(euclidianViewPanel, euclidiancontroller, evNo,
settings);
initClickStartHandler();
}
private void initClickStartHandler() {
ClickStartHandler.init(g2p.getCanvas(), new ClickStartHandler() {
@Override
public void onClickStart(final int x, final int y,
PointerEventType type) {
getEuclidianController().closePopups(x, y, type);
}
});
}
/**
* @param euclidiancontroller
* controller
* @param viewNo
* view number
* @param settings
* settings
*/
public EuclidianViewW(EuclidianController euclidiancontroller, int viewNo,
EuclidianSettings settings) {
super(euclidiancontroller, viewNo, settings);
viewTextField = new ViewTextFieldW(this);
EVPanel = newMyEuclidianViewPanel();
// It seems this constructor is only called from PlotPanelEuclidianViewW
// currently,
// so this -1 is changed to viewNo because EVNO_GENERAL is needed for
// making sure that this view does not change the toolbar, code in
// EuclidianControllerW
// if you think it is not Okay, then use (-1) in EuclidianControllerW
// instead of EVNO_GENERAL
// at mouse events which call setActiveToolbarId #plotpanelevno
// initBaseComponents(EVPanel, euclidiancontroller, -1);
initBaseComponents(EVPanel, euclidiancontroller, viewNo, settings);
initClickStartHandler();
}
@Override
public final GFont getFont() {
return new GFontW(g2p.getFont());
}
@Override
public final GColor getBackgroundCommon() {
return backgroundColor;
}
@Override
public final void setBackground(GColor bgColor) {
if (bgColor != null) {
backgroundColor = GColor.newColor(bgColor.getRed(),
bgColor.getGreen(), bgColor.getBlue(), bgColor.getAlpha());
}
}
@Override
public final GGraphics2D getTempGraphics2D(GFont fontForGraphics) {
if (this.g2dtemp == null) {
this.g2dtemp = new GGraphics2DW(Canvas.createIfSupported());
}
this.g2dtemp.setFont(fontForGraphics);
return this.g2dtemp;
}
@Override
protected final MyZoomer newZoomer() {
return new MyZoomerW(this);
}
@Override
public final void paintBackground(GGraphics2D g2) {
if (isGridOrAxesShown() || hasBackgroundImages() || isTraceDrawn()
|| app.showResetIcon()
|| kernel.needToShowAnimationButton()) {
((GGraphics2DW) g2).drawImage(bgImage,
0, 0);
} else {
((GGraphics2DW) g2).fillWith(getBackgroundCommon());
}
}
/**
* This doRepaint method should be used instead of repaintView in cases when
* the repaint should be done immediately
*/
public final void doRepaint2() {
long time = System.currentTimeMillis();
this.updateBackgroundIfNecessary();
paint(this.g2p);
// if we have pen tool in action
// repaint the preview line
getEuclidianController().setCollectedRepaints(false);
lastRepaint = System.currentTimeMillis() - time;
GeoGebraProfiler.addRepaint(lastRepaint);
}
/**
* Gets the coordinate space width of the <canvas>.
*
* @return the logical width
*/
@Override
public int getWidth() {
return (int) (this.g2p.getCoordinateSpaceWidth() / this.g2p.devicePixelRatio);
}
/**
* Gets the coordinate space height of the <canvas>.
*
* @return the logical height
*/
@Override
public int getHeight() {
return (int) (this.g2p.getCoordinateSpaceHeight() / this.g2p.devicePixelRatio);
}
@Override
public void clearView() {
resetLists();
updateBackgroundImage(); // clear traces and images
// resetMode();
if (app.getGuiManager() != null) {
app.getGuiManager().clearAbsolutePanels();
}
removeTextField();
}
@Override
protected final void setHeight(int h) {
// TODO: not clear what should we do
}
@Override
protected final void setWidth(int h) {
// TODO: not clear what should we do
}
@Override
public final GGraphics2DW getGraphicsForPen() {
return g2p;
}
@Override
public final boolean isShowing() {
return g2p != null && g2p.getCanvas() != null
&& g2p.getCanvas().isAttached() && g2p.getCanvas().isVisible();
}
@Override
public String getExportImageDataUrl(double scale, boolean transparency) {
int width = (int) Math.floor(getExportWidth() * scale);
int height = (int) Math.floor(getExportHeight() * scale);
Canvas c4 = Canvas.createIfSupported();
c4.setCoordinateSpaceWidth(width);
c4.setCoordinateSpaceHeight(height);
c4.setWidth(width + "px");
c4.setHeight(height + "px");
g4copy = new GGraphics2DW(c4);
this.app.setExporting(ExportType.PNG, scale);
exportPaintPre(g4copy, scale, transparency);
drawObjects(g4copy);
this.app.setExporting(ExportType.NONE, 1);
return g4copy.getCanvas().toDataUrl();
}
public String getExportSVG(double scale, boolean transparency) {
int width = (int) Math.floor(getExportWidth() * scale);
int height = (int) Math.floor(getExportHeight() * scale);
JavaScriptObject ctx = getCanvas2SVG(width, height);
if (ctx == null) {
Log.debug("canvas2SVG not found");
return null;
}
g4copy = new GGraphics2DW((Context2d) ctx.cast());
this.app.setExporting(ExportType.SVG, scale);
exportPaintPre(g4copy, scale, transparency);
drawObjects(g4copy);
this.app.setExporting(ExportType.NONE, 1);
return getSerializedSvg(ctx);
}
private native JavaScriptObject getCanvas2SVG(double width,
double height) /*-{
if ($wnd.C2S) {
return new $wnd.C2S(width, height);
}
return null;
}-*/;
private native String getSerializedSvg(JavaScriptObject ctx) /*-{
return ctx.getSerializedSvg(true);
}-*/;
@Override
public GBufferedImageW getExportImage(double scale) {
return getExportImage(scale, false);
}
public GBufferedImageW getExportImage(double scale, boolean transparency) {
int width = (int) Math.floor(getExportWidth() * scale);
int height = (int) Math.floor(getExportHeight() * scale);
GBufferedImageW img = new GBufferedImageW(width, height, 1, true);
exportPaint(new GGraphics2DW(img.getCanvas()), scale, transparency,
ExportType.PNG);
return img;
}
/**
* @param canvas
* canvas
* @param scale
* ratio of desired size and current size of the graphics
*/
public void exportPaint(Canvas canvas, double scale) {
exportPaint(new GGraphics2DW(canvas), scale, false, ExportType.PNG);
}
/**
* repaintView just calls this method
*/
@Override
public void repaint() {
// TODO: this is a temporary hack until the timer system can handle
// TextPreview view
// (or ignore timer system because text preview only draws one geo)
if (getViewID() == App.VIEW_TEXT_PREVIEW || getViewID() < 0) {
doRepaint();
return;
}
if (getEuclidianController().isCollectingRepaints()) {
getEuclidianController().setCollectedRepaints(true);
return;
}
getApplication().ensureTimerRunning();
if (waitForRepaint == TimerSystemW.SLEEPING_FLAG) {
waitForRepaint = TimerSystemW.EUCLIDIAN_LOOPS;
}
}
private int waitForRepaint = TimerSystemW.SLEEPING_FLAG;
/**
* schedule a repaint
*/
public void doRepaint() {
repaintScheduler.requestAnimationFrame(repaintCallback);
}
/**
* timer system suggests a repaint
*/
@Override
public boolean suggestRepaint() {
if (waitForRepaint == TimerSystemW.SLEEPING_FLAG) {
return false;
}
if (waitForRepaint == TimerSystemW.REPAINT_FLAG) {
if (isShowing()) {
doRepaint();
waitForRepaint = TimerSystemW.SLEEPING_FLAG;
}
return true;
}
waitForRepaint--;
return true;
}
public void setCoordinateSpaceSize(int width, int height) {
g2p.setCoordinateSpaceSize(width, height);
try {
// just resizing the AbsolutePanelSmart, not the whole of DockPanel
g2p.getCanvas().getElement().getParentElement().getStyle()
.setWidth(width, Style.Unit.PX);
g2p.getCanvas().getElement().getParentElement().getStyle()
.setHeight(height, Style.Unit.PX);
getEuclidianController().calculateEnvironment();
} catch (Exception exc) {
Log.debug("Problem with the parent element of the canvas");
}
}
public void synCanvasSize() {
setCoordinateSpaceSize(g2p.getOffsetWidth(), g2p.getOffsetHeight());
}
@Override
public String getCanvasBase64WithTypeString() {
return getCanvasBase64WithTypeString(g2p.getCoordinateSpaceWidth(),
g2p.getCoordinateSpaceHeight(), bgGraphics == null ? null
: ((GGraphics2DW) bgGraphics).getCanvas(),
g2p.getCanvas());
}
public static String getCanvasBase64WithTypeString(double width,
double height, Canvas background, Canvas foreground) {
// TODO: make this more perfect, like in Desktop
double ratio = width / height;
double thx = MyXMLio.THUMBNAIL_PIXELS_X;
double thy = MyXMLio.THUMBNAIL_PIXELS_Y;
if (ratio < 1) {
thx *= ratio;
} else if (ratio > 1) {
thy /= ratio;
}
Canvas canv = Canvas.createIfSupported();
canv.setCoordinateSpaceHeight((int) thy);
canv.setCoordinateSpaceWidth((int) thx);
canv.setWidth((int) thx + "px");
canv.setHeight((int) thy + "px");
Context2d c2 = canv.getContext2d();
// g2p.getCanvas().getContext2d().drawImage(((GGraphics2DW)bgGraphics).getCanvas().getCanvasElement(),
// 0, 0, (int)thx, (int)thy);
if (background != null) {
c2.drawImage(background.getCanvasElement(), 0, 0, (int) thx,
(int) thy);
}
c2.drawImage(foreground.getCanvasElement(), 0, 0, (int) thx,
(int) thy);
return canv.toDataUrl();
}
@Override
protected void updateSizeKeepDrawables() {
if ((getWidth() <= 0) || (getHeight() <= 0)) {
return;
}
// real world values
companion.setXYMinMaxForUpdateSize();
setRealWorldBounds();
try {
createImage();
} catch (Exception e) {
bgImage = null;
bgGraphics = null;
}
updateBackgroundImage();
}
public void createImage() {
bgImage = new GBufferedImageW(g2p.getOffsetWidth(),
g2p.getOffsetHeight(),
app == null ? 1 : app
.getPixelRatio(),
false);
bgGraphics = bgImage.createGraphics();
}
@Override
public double getMinSamplePoints() {
return 40;
}
/*
* public double getMaxBendOfScreen() { return MAX_BEND_OFF_SCREEN; }*
*
* public double getMaxBend() { return MAX_BEND; }
*
* public int getMaxDefinedBisections() { return MAX_DEFINED_BISECTIONS; }
*/
@Override
public double getMinPixelDistance() {
return this.g2p == null || g2p.getScale() <= 1 ? 0.5 : 1;
}
/*
* public int getMaxZeroCount() { return MAX_ZERO_COUNT; }
*/
@Override
public double getMaxPixelDistance() {
return this.g2p == null || g2p.getScale() <= 1 ? 15 : 30;
}
/*
* public int getMaxProblemBisections() { return MAX_PROBLEM_BISECTIONS; }
*/
@Override
public long getLastRepaintTime() {
return lastRepaint;
}
/**
* @return new panel
*/
protected MyEuclidianViewPanel newMyEuclidianViewPanel() {
return new MyEuclidianViewPanel(this);
}
private void initBaseComponents(EuclidianPanelWAbstract euclidianViewPanel,
EuclidianController euclidiancontroller, int newEvNo,
EuclidianSettings settings) {
final Canvas canvas = euclidianViewPanel.getCanvas();
this.evNo = newEvNo;
this.g2p = new GGraphics2DW(canvas);
g2p.devicePixelRatio = app.getPixelRatio();
g2p.setView(this);
updateFonts();
initView(true);
attachView();
((EuclidianControllerW) euclidiancontroller).setView(this);
if (this.getViewID() != App.VIEW_TEXT_PREVIEW) {
registerKeyHandlers(canvas);
registerMouseTouchGestureHandlers(euclidianViewPanel,
(EuclidianControllerW) euclidiancontroller);
}
registerDragDropHandlers(euclidianViewPanel,(EuclidianControllerW) euclidiancontroller);
updateFirstAndLast(true, true);
canvas.addAttachHandler(new AttachEvent.Handler() {
@Override
public void onAttachOrDetach(AttachEvent ae) {
if (ae.isAttached()) {
// canvas just attached
// if (canvas.isVisible()) {
// if the canvas is set to visible,
// we're also going to call this
// but it seems the canvas is never
// made invisible now (otherwise
// we would need to override it maybe)
// ... it is a good question whether the
// respective methods of DockManagerW, i.e.
// show, hide, maximize and drop call this?
updateFirstAndLast(true, false);
// }
} else {
// canvas just detached
// here lazy update shall happen!
// i.e. focus handler shall update
// firstInstance and lastInstance
// BUT also we shall make them null now
updateFirstAndLast(false, false);
}
}
});
canvas.addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent be) {
focusLost();
cycle(EuclidianViewW.this);
}
});
canvas.addFocusHandler(new FocusHandler() {
@Override
public void onFocus(FocusEvent fe) {
focusGained();
EuclidianViewW.selectNextGeoOnTab(EuclidianViewW.this);
}
});
// euclidianViewPanel.addDomHandler((EuclidianController)euclidiancontroller,
// KeyPressEvent.getType());
// euclidianViewPanel.addKeyDownHandler(this.app.getGlobalKeyDispatcher());
// euclidianViewPanel.addKeyUpHandler(this.app.getGlobalKeyDispatcher());
// euclidianViewPanel.addKeyPressHandler(this.app.getGlobalKeyDispatcher());
EuclidianSettings es = null;
if (settings != null) {
es = settings;
// settings from XML for EV1, EV2
// not for eg probability calculator
} else if ((newEvNo == 1) || (newEvNo == 2)) {
es = getApplication().getSettings().getEuclidian(newEvNo);
}
if (es != null) {
settingsChanged(es);
es.addListener(this);
}
addDummyDiv();
}
/**
* @param ev
* @param anyway
*/
static final public void updateFirstAndLast(EuclidianViewWInterface ev,
boolean anyway) {
ev.getCanvas().setTabIndex(GeoGebraFrameW.GRAPHICS_VIEW_TABINDEX);
if (firstInstance == null) {
firstInstance = ev;
} else if (ev.getCanvas().isAttached()) {
if (compareDocumentPosition(ev.getCanvas().getCanvasElement(),
firstInstance.getCanvas().getCanvasElement())) {
firstInstance = ev;
}
} else if (anyway) {
// then compare to something equivalent!
// if we are in different applet;
// ... anything from this applet is right
// if we are in the same applet;
// ... does it matter? (yes, but just a little bit)
// TODO: to be fixed in a better way later,
// after it is seen whether this is really a little fix...
if (compareDocumentPosition(
((AppW) ev.getApplication()).getFrameElement(),
firstInstance
.getCanvas().getCanvasElement())) {
firstInstance = ev;
}
}
if (lastInstance == null) {
lastInstance = ev;
} else if (ev.getCanvas().isAttached()) {
if (compareDocumentPosition(lastInstance.getCanvas()
.getCanvasElement(), ev.getCanvas().getCanvasElement())) {
lastInstance = ev;
}
} else if (anyway) {
if (compareDocumentPosition(lastInstance.getCanvas()
.getCanvasElement(),
((AppW) ev.getApplication()).getFrameElement())) {
lastInstance = ev;
}
}
}
@Override
public void updateFirstAndLast(boolean attach, boolean anyway) {
if (attach) {
if ((evNo == 1) || (evNo == 2) || isViewForPlane()) {
updateFirstAndLast(this, anyway);
} else {
// is this the best?
getCanvas().setTabIndex(
GeoGebraFrameW.GRAPHICS_VIEW_TABINDEX - 1);
}
} else {
// ?
}
}
/**
* Used for comparing position in DOM (Document Object Model)
*
* @param firstElement
* it is right if this comes first
* @param secondElement
* it is right if this comes second
* @return whether firstElement is really before secondElement
*/
public static native boolean compareDocumentPosition(
Element firstElement,
Element secondElement) /*-{
if (firstElement) {
if (secondElement) {
if (firstElement === secondElement) {
// let's interpret it as false result
return false;
}
if (firstElement.compareDocumentPosition(secondElement)
& $wnd.Node.DOCUMENT_POSITION_FOLLOWING) {
return true;
}
// if any of them contain the other, let us interpret
// as false result, and anyway, this shall not happen!
// but probably this is DOCUMENT_POSITION_PRECEDING:
return false;
}
}
return false;
}-*/;
private void registerKeyHandlers(Canvas canvas) {
canvas.addKeyDownHandler(this.app.getGlobalKeyDispatcher());
canvas.addKeyUpHandler(this.app.getGlobalKeyDispatcher());
canvas.addKeyPressHandler(this.app.getGlobalKeyDispatcher());
}
private void registerMouseTouchGestureHandlers(
EuclidianPanelWAbstract euclidianViewPanel,
EuclidianControllerW euclidiancontroller) {
Widget evPanel = euclidianViewPanel.getAbsolutePanel();
evPanel.addDomHandler(euclidiancontroller, MouseWheelEvent.getType());
if (!app.has(Feature.PEN_EVENTS)
|| !Browser.supportsPointerEvents(true)) {
evPanel.addDomHandler(euclidiancontroller,
MouseMoveEvent.getType());
evPanel.addDomHandler(euclidiancontroller,
MouseOverEvent.getType());
evPanel.addDomHandler(euclidiancontroller, MouseOutEvent.getType());
evPanel.addDomHandler(euclidiancontroller, MouseUpEvent.getType());
if (app.getLAF() == null || !app.getLAF().isSmart()) {
evPanel.addDomHandler(euclidiancontroller,
MouseDownEvent.getType());
}
}
if (Browser.supportsPointerEvents(app.has(Feature.PEN_EVENTS))) {
pointerHandler = new PointerEventHandler((IsEuclidianController) euclidianController,
euclidiancontroller.getOffsets());
PointerEventHandler.attachTo(evPanel.getElement(), pointerHandler,
app.has(Feature.PEN_EVENTS));
if (app.has(Feature.PEN_EVENTS)) {
CancelEventTimer.killTouch(evPanel);
}
return;
}
if (app.getLAF() != null) {
if (app.getLAF().registerHandlers(evPanel, euclidiancontroller)) {
return;
}
}
evPanel.addDomHandler(euclidiancontroller, TouchStartEvent.getType());
evPanel.addDomHandler(euclidiancontroller, TouchEndEvent.getType());
evPanel.addDomHandler(euclidiancontroller, TouchMoveEvent.getType());
evPanel.addDomHandler(euclidiancontroller, TouchCancelEvent.getType());
evPanel.addDomHandler(euclidiancontroller, GestureStartEvent.getType());
evPanel.addDomHandler(euclidiancontroller, GestureChangeEvent.getType());
evPanel.addDomHandler(euclidiancontroller, GestureEndEvent.getType());
}
private static void registerDragDropHandlers(
EuclidianPanelWAbstract euclidianViewPanel,
EuclidianControllerW euclidiancontroller) {
Widget evPanel = euclidianViewPanel.getAbsolutePanel();
evPanel.addDomHandler(euclidiancontroller, DropEvent.getType());
}
// STROKES
final protected static GBasicStrokeW standardStroke = new GBasicStrokeW(
1.0, GBasicStroke.CAP_ROUND, GBasicStroke.JOIN_ROUND);
final protected static GBasicStrokeW selStroke = new GBasicStrokeW(
1.0 + EuclidianStyleConstants.SELECTION_ADD,
GBasicStroke.CAP_ROUND, GBasicStroke.JOIN_ROUND);
protected boolean unitAxesRatio;
private Object preferredSize;
private SimplePanel dummyDiv;
static public GBasicStrokeW getDefaultStroke() {
return standardStroke;
}
static public GBasicStrokeW getDefaultSelectionStroke() {
return selStroke;
}
/* Code for dashed lines removed in r23713 */
/**
* Gets pixel width of the <canvas>.
*
* @return the physical width in pixels
*/
public int getPhysicalWidth() {
return g2p.getOffsetWidth();
}
/**
* Gets pixel height of the <canvas>.
*
* @return the physical height in pixels
*/
public int getPhysicalHeight() {
return g2p.getOffsetHeight();
}
@Override
public int getAbsoluteTop() {
return g2p.getAbsoluteTop();
}
@Override
public int getAbsoluteLeft() {
return g2p.getAbsoluteLeft();
}
@Override
public EuclidianControllerW getEuclidianController() {
return (EuclidianControllerW) euclidianController;
}
@Override
protected void initCursor() {
setDefaultCursor();
}
@Override
protected void setStyleBarMode(int mode) {
if (hasStyleBar()) {
getStyleBar().setMode(mode);
}
}
private ImageElement getResetImage() {
if (resetImage == null) {
resetImage = this.app.getRefreshViewImage();
}
return resetImage;
}
private ImageElement getPlayImage(boolean highlight) {
if (playImage == null) {
playImage = this.app.getPlayImage();
playImageHL = this.app.getPlayImageHover();
}
return highlight ? playImageHL : playImage;
}
private ImageElement getPauseImage(boolean highlight) {
if (pauseImage == null) {
pauseImage = this.app.getPauseImage();
pauseImageHL = this.app.getPauseImageHover();
}
return highlight ? pauseImageHL : pauseImage;
}
@Override
public boolean hitAnimationButton(int x, int y) {
// draw button in focused EV only
if (!drawPlayButtonInThisView()) {
return false;
}
return kernel.needToShowAnimationButton() && (x <= 27)
&& (y >= (getHeight() - 27));
}
@Override
public boolean requestFocusInWindow() {
g2p.getCanvas().getCanvasElement().focus();
focusGained();
return true;
}
public void focusLost() {
if (isInFocus) {
this.isInFocus = false;
if (getCanvas() != null) {
// this.app.focusLost(this, getCanvas().getElement());
}
}
}
public void focusGained() {
if (!isInFocus) {
this.isInFocus = true;
if (getCanvas() != null) {
this.app.focusGained(this, getCanvas().getElement());
}
}
}
@Override
public boolean isInFocus() {
return isInFocus;
}
public void setDefaultCursor() {
setCursorClass("cursor_default");
}
private void setCursorClass(String className) {
// IMPORTANT: do nothing if we already have the classname,
// app.resetCursor is VERY expensive in IE
if (!g2p.getCanvas().getElement().hasClassName(className)) {
this.app.resetCursor();
g2p.getCanvas().setStyleName("");
g2p.getCanvas().addStyleName(className);
}
}
public void setHitCursor() {
setCursorClass("cursor_hit");
}
@Override
protected EuclidianStyleBar newEuclidianStyleBar() {
if (app.getGuiManager() == null) {
return null;
}
return app.getGuiManager().newEuclidianStylebar(this, this.getViewID());
}
@Override
protected void addDynamicStylebarToEV(EuclidianStyleBar dynamicStylebar) {
if (((Widget) dynamicStylebar).getParent() == null) {
app.getGuiManager().addStylebar(this, dynamicStylebar);
}
}
@Override
protected EuclidianStyleBar newDynamicStyleBar() {
return app.getGuiManager().newDynamicStylebar(this);
}
@Override
final protected void drawAnimationButtons(final GGraphics2D g2) {
// draw button in focused EV only
if (!drawPlayButtonInThisView() || app.isScreenshotGenerator()) {
return;
}
final int x = 3;
final int y = getHeight() - 27;
// draw pause or play button
final ImageElement img = kernel.isAnimationRunning()
? getPauseImage(highlightAnimationButtons)
: getPlayImage(highlightAnimationButtons);
if (img.getPropertyBoolean("complete")) {
((GGraphics2DW) g2).drawImage(img, x, y);
} else {
ImageWrapper.nativeon(img, "load", new ImageLoadCallback() {
@Override
public void onLoad() {
((GGraphics2DW) g2).drawImage(img, x, y);
}
});
}
}
@Override
public void setPreferredSize(GDimension preferredSize) {
if (this.preferredSize != null
&& this.preferredSize.equals(preferredSize)) {
return;
}
this.preferredSize = preferredSize;
g2p.setPreferredSize(preferredSize);
updateSize();
setReIniting(false);
}
/**
* Updates the size of the canvas and coordinate system
*
* @param width
* the new width (in pixel)
* @param height
* the new height (in pixel)
*/
public void setPreferredSize(int width, int height) {
setPreferredSize(new GDimensionW(width, height));
}
private void setDragCursor() {
if (this.app.useTransparentCursorWhenDragging()) {
setCursorClass("cursor_transparent");
} else {
setCursorClass("cursor_drag");
}
}
@Override
public void setToolTipText(String plainTooltip) {
ToolTipManagerW.sharedInstance().showToolTip(plainTooltip);
}
private void setResizeXAxisCursor() {
setCursorClass("cursor_resizeXAxis");
}
private void setResizeYAxisCursor() {
setCursorClass("cursor_resizeYAxis");
}
private void setResizeNESWCursor() {
setCursorClass("cursor_resizeNESW");
}
private void setResizeNWSECursor() {
setCursorClass("cursor_resizeNWSE");
}
private void setResizeEWCursor() {
setCursorClass("cursor_resizeEW");
}
private void setResizeNSCursor() {
setCursorClass("cursor_resizeNS");
}
private void setMoveCursor() {
setCursorClass("cursor_move");
}
private void setTransparentCursor() {
setCursorClass("cursor_transparent");
}
private void setEraserCursor() {
setCursorClass("cursor_eraser");
}
private void setPenCursor() {
setCursorClass("cursor_pen");
}
@Override
public boolean hasFocus() {
// changed to return true, otherwise Arrow keys don't work to pan the
// view, see GlobalKeyDispatcher
// return isInFocus;
return true;
}
@Override
public void add(GBox box) {
if (EVPanel != null) {
EVPanel.getAbsolutePanel().add(GBoxW.getImpl(box),
(int) box.getBounds().getX(), (int) box.getBounds().getY());
}
}
@Override
public void remove(GBox box) {
if (EVPanel != null) {
EVPanel.getAbsolutePanel().remove(GBoxW.getImpl(box));
}
}
@Override
protected void drawResetIcon(GGraphics2D g) {
int w = getWidth();
((GGraphics2DW) g).getCanvas().getContext2d()
.drawImage(getResetImage(), w - 24, 2);
}
/* needed because set the id of canvas */
@Override
public void setEuclidianViewNo(int evNo) {
if (evNo >= 2) {
this.evNo = evNo;
}
}
@Override
public void requestFocus() {
// this may be really necessary preventing a tabbing away issue
// but the reasons of it are not well understood #5158
// after better understanding, this can probably be merged
// with the following method (requestFocusInWindow()):
// TODO: or, one method shall do this for sure, the other one
// should do this only when isInFocus is false
requestFocusInWindow();
}
@Override
public void resetPointerEventHandler() {
if (pointerHandler != null) {
pointerHandler.reset();
}
}
@Override
public Canvas getCanvas() {
return g2p.getCanvas();
}
@Override
public GGraphics2DW getG2P() {
return g2p;
}
@Override
public void setPixelRatio(double pixelRatio) {
if (Kernel.isEqual(g2p.devicePixelRatio, pixelRatio)) {
return;
}
int realWidth = g2p.getOffsetWidth();
int realHeight = g2p.getOffsetHeight();
g2p.devicePixelRatio = pixelRatio;
if (realHeight > 0 && realWidth > 0) {
g2p.setCoordinateSpaceSize(realWidth, realHeight);
this.createImage();
this.updateBackground();
repaint();
}
}
@Override
public void setAltText() {
GeoElement alt = app.getKernel().lookupLabel("altText" + evNo);
if (alt == null) {
alt = app.getKernel().lookupLabel("altText");
}
if (alt instanceof GeoText) {
String altStr = ((GeoText) alt).getTextString();
if (g2p.setAltText(altStr)) {
this.readText(altStr);
}
}
}
@Override
public void closeDropdowns() {
closeAllDropDowns();
}
@Override
public void cancelBlur() {
CancelEventTimer.disableBlurEvent();
}
@Override
public void getPrintable(final FlowPanel pPanel, Button btPrint) {
Log.debug(getPrintingScale());
final Image prevImg = new Image();
String urlText = getExportImageDataUrl(getPrintingScale(), false);
prevImg.getElement().setAttribute("src", urlText);
prevImg.addStyleName("prevImg");
pPanel.clear();
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
pPanel.add(prevImg);
Window.print();
// PrintPreviewW.removePrintPanelFromDOM();
NodeList<com.google.gwt.dom.client.Element> pp = Dom
.getElementsByClassName("printPanel");
if (pp.getLength() != 0) {
pp.getItem(0).removeFromParent();
}
}
});
}
@Override
public double getPixelRatio() {
return app.getPixelRatio();
}
@Override
public void setCursor(EuclidianCursor cursor) {
switch (cursor) {
case HIT:
setHitCursor();
return;
case DRAG:
setDragCursor();
return;
case MOVE:
setMoveCursor();
return;
case DEFAULT:
setDefaultCursor();
return;
case RESIZE_X:
setResizeXAxisCursor();
return;
case RESIZE_Y:
setResizeYAxisCursor();
return;
case RESIZE_NESW:
setResizeNESWCursor();
return;
case RESIZE_NWSE:
setResizeNWSECursor();
return;
case RESIZE_EW:
setResizeEWCursor();
return;
case RESIZE_NS:
setResizeNSCursor();
return;
case TRANSPARENT:
setTransparentCursor();
return;
case ERASER:
if (app.isWhiteboardActive() && getEuclidianController()
.getDefaultEventType() != PointerEventType.MOUSE) {
setTransparentCursor();
} else {
setEraserCursor();
}
return;
case PEN:
// Log.debug("event type: " +
// getEuclidianController().getDefaultEventType());
if (app.isWhiteboardActive() && getEuclidianController()
.getDefaultEventType() != PointerEventType.MOUSE) {
setTransparentCursor();
} else {
setPenCursor();
}
return;
}
}
private void addDummyDiv() {
dummyDiv = new SimplePanel();
// can't be tabbed, but can get the focus programmatically
dummyDiv.getElement().setTabIndex(-1);
RootPanel.get().add(dummyDiv);
dummyDiv.getElement().setAttribute("role", "status");
dummyDiv.getElement().setAttribute("aria-live", "polite");
dummyDiv.getElement().setAttribute("aria-atomic", "true");
dummyDiv.getElement().setAttribute("aria-relevant", "text");
dummyDiv.getElement().getStyle().setTop(-1000.0, Unit.PX);
dummyDiv.getElement().getStyle().setPosition(Position.ABSOLUTE);
}
boolean temp = true;
@Override
public void readText(final String text) {
Log.debug("read text: " + text);
dummyDiv.getElement().setInnerText(text);
dummyDiv.getElement().focus();
g2p.getCanvas().getCanvasElement().focus();
}
public static native void printFocusedElement()/*-{
$wnd.console.log($doc.activeElement);
}-*/;
public static void cycle(EuclidianView from) {
if ((from == EuclidianViewW.lastInstance)
&& EuclidianViewW.tabPressed) {
// if this is the last to blur, and tabPressed
// is true, i.e. want to select another applet,
// let's go back to the first one!
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
// in theory, the tabPressed will not be set
// to false
// before this, because the element that
// naturally
// receives focus will not be an
// EuclidianView,
// for this is the last one, but why not
// make sure?
EuclidianViewW.tabPressed = true;
// probably we have to wait for the
// focus event that accompanies this
// blur first, and only request for
// new focus afterwards...
EuclidianViewW.firstInstance.requestFocus();
}
});
}
}
public static boolean checkTabPress(boolean success) {
if (!success) {
// should select first GeoElement in next applet
// this should work well except from last to first
// so there will be a blur handler there
// it would be too hard to select the first GeoElement
// from here, so this will be done in the focus handler
// of the other applet, depending on whether really
// this code called it, and it can be done by a static
// variable for the short term
EuclidianViewW.tabPressed = true;
// except EuclidianViewW.lastInstance, do not prevent:
if (EuclidianViewW.lastInstance.isInFocus()) {
EuclidianViewW.lastInstance.getCanvas().getElement().blur();
return true;
}
return false;
}
EuclidianViewW.tabPressed = false;
return true;
}
public static void selectNextGeoOnTab(EuclidianView view) {
if (EuclidianViewW.tabPressed) {
// if focus is moved here from another applet,
// select the first GeoElement of this Graphics view
EuclidianViewW.tabPressed = false;
view.getApplication().getSelectionManager().selectNextGeo(view,
true);
// .setFirstGeoSelectedForPropertiesView(); might not be
// perfect,
// for that GeoElement might not be visible in all Graphics
// views
}
}
public static void resetTab() {
tabPressed = false;
}
}