package org.geogebra.web.html5.main; import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; import org.geogebra.common.GeoGebraConstants; import org.geogebra.common.GeoGebraConstants.Versions; import org.geogebra.common.awt.GDimension; import org.geogebra.common.awt.GFont; import org.geogebra.common.awt.MyImage; import org.geogebra.common.euclidian.DrawEquation; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.euclidian.EuclidianController; import org.geogebra.common.euclidian.EuclidianView; import org.geogebra.common.export.pstricks.GeoGebraToAsymptote; import org.geogebra.common.export.pstricks.GeoGebraToPgf; import org.geogebra.common.export.pstricks.GeoGebraToPstricks; import org.geogebra.common.factories.AwtFactory; import org.geogebra.common.factories.CASFactory; import org.geogebra.common.factories.Factory; import org.geogebra.common.factories.FormatFactory; import org.geogebra.common.factories.UtilFactory; import org.geogebra.common.gui.SetLabels; import org.geogebra.common.gui.view.algebra.AlgebraView; import org.geogebra.common.gui.view.algebra.AlgebraView.SortMode; import org.geogebra.common.io.MyXMLio; import org.geogebra.common.io.layout.Perspective; import org.geogebra.common.javax.swing.GImageIcon; import org.geogebra.common.javax.swing.GOptionPane; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.GeoFactory; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Macro; import org.geogebra.common.kernel.UndoManager; import org.geogebra.common.kernel.View; import org.geogebra.common.kernel.barycentric.AlgoCubicSwitch; import org.geogebra.common.kernel.barycentric.AlgoKimberlingWeights; import org.geogebra.common.kernel.commands.CommandDispatcher; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoElementGraphicsAdapter; import org.geogebra.common.kernel.geos.GeoImage; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.main.AlgoCubicSwitchInterface; import org.geogebra.common.main.AlgoCubicSwitchParams; import org.geogebra.common.main.AlgoKimberlingWeightsInterface; import org.geogebra.common.main.AlgoKimberlingWeightsParams; import org.geogebra.common.main.App; import org.geogebra.common.main.DialogManager; import org.geogebra.common.main.Feature; import org.geogebra.common.main.FontManager; import org.geogebra.common.main.GeoElementSelectionListener; import org.geogebra.common.main.MaterialsManagerI; import org.geogebra.common.main.MyError; import org.geogebra.common.main.SpreadsheetTableModel; import org.geogebra.common.main.error.ErrorHandler; import org.geogebra.common.main.settings.AlgebraSettings; import org.geogebra.common.main.settings.EuclidianSettings; import org.geogebra.common.move.events.BaseEventPool; import org.geogebra.common.move.events.NativeEventAttacher; import org.geogebra.common.move.ggtapi.models.ClientInfo; import org.geogebra.common.move.ggtapi.models.Material; import org.geogebra.common.move.ggtapi.models.Material.Provider; import org.geogebra.common.move.ggtapi.operations.LogInOperation; import org.geogebra.common.move.operations.Network; import org.geogebra.common.move.operations.NetworkOperation; import org.geogebra.common.move.views.OfflineView; import org.geogebra.common.plugin.ScriptManager; import org.geogebra.common.plugin.SensorLogger; import org.geogebra.common.sound.SoundManager; import org.geogebra.common.util.AsyncOperation; import org.geogebra.common.util.FileExtensions; import org.geogebra.common.util.GTimer; import org.geogebra.common.util.GTimerListener; import org.geogebra.common.util.MD5EncrypterGWTImpl; import org.geogebra.common.util.NormalizerMinimal; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.debug.Log; import org.geogebra.common.util.lang.Language; import org.geogebra.web.html5.Browser; import org.geogebra.web.html5.awt.GDimensionW; import org.geogebra.web.html5.awt.GFontW; import org.geogebra.web.html5.css.GuiResourcesSimple; import org.geogebra.web.html5.euclidian.EuclidianControllerW; import org.geogebra.web.html5.euclidian.EuclidianPanelWAbstract; import org.geogebra.web.html5.euclidian.EuclidianViewW; import org.geogebra.web.html5.euclidian.EuclidianViewWInterface; import org.geogebra.web.html5.euclidian.MouseTouchGestureControllerW; import org.geogebra.web.html5.export.GeoGebraToAsymptoteW; import org.geogebra.web.html5.export.GeoGebraToPgfW; import org.geogebra.web.html5.export.GeoGebraToPstricksW; import org.geogebra.web.html5.factories.AwtFactoryW; import org.geogebra.web.html5.factories.FactoryW; import org.geogebra.web.html5.factories.FormatFactoryW; import org.geogebra.web.html5.factories.UtilFactoryW; import org.geogebra.web.html5.gui.AlgebraInput; import org.geogebra.web.html5.gui.GPopupPanel; import org.geogebra.web.html5.gui.GuiManagerInterfaceW; import org.geogebra.web.html5.gui.LoadingApplication; import org.geogebra.web.html5.gui.ToolBarInterface; import org.geogebra.web.html5.gui.laf.GLookAndFeelI; import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW; import org.geogebra.web.html5.gui.util.MathKeyboardListener; import org.geogebra.web.html5.gui.util.ViewsChangedListener; import org.geogebra.web.html5.io.ConstructionException; import org.geogebra.web.html5.io.MyXMLioW; import org.geogebra.web.html5.javax.swing.GOptionPaneW; import org.geogebra.web.html5.kernel.GeoElementGraphicsAdapterW; import org.geogebra.web.html5.kernel.UndoManagerW; import org.geogebra.web.html5.kernel.commands.CommandDispatcherW; import org.geogebra.web.html5.move.googledrive.GoogleDriveOperation; import org.geogebra.web.html5.sound.GTimerW; import org.geogebra.web.html5.sound.SoundManagerW; import org.geogebra.web.html5.util.ArticleElement; import org.geogebra.web.html5.util.DynamicScriptElement; import org.geogebra.web.html5.util.ImageManagerW; import org.geogebra.web.html5.util.ScriptLoadCallback; import org.geogebra.web.html5.util.SpreadsheetTableModelW; import org.geogebra.web.html5.util.UUIDW; import org.geogebra.web.html5.util.ViewW; import org.geogebra.web.html5.util.debug.GeoGebraProfilerW; import org.geogebra.web.plugin.WebsocketLogger; import org.geogebra.web.resources.JavaScriptInjector; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.core.client.RunAsyncCallback; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.storage.client.Storage; import com.google.gwt.storage.client.StorageMap; import com.google.gwt.user.client.Cookies; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HeaderPanel; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.Widget; @SuppressWarnings("javadoc") public abstract class AppW extends App implements SetLabels { public static final String STORAGE_MACRO_KEY = "storedMacro"; public static final String STORAGE_MACRO_ARCHIVE = "macroArchive"; public static final String DEFAULT_APPLET_ID = "ggbApplet"; private DrawEquationW drawEquation; private NormalizerMinimal normalizerMinimal; private GgbAPIW ggbapi; private final LocalizationW loc; private ImageManagerW imageManager; private HashMap<String, String> currentFile = null; // random id to identify ggb files // eg so that GeoGebraTube can notice it's a version of the same file private int localID = -1; private long syncStamp; protected GoogleDriveOperation googleDriveOperation; private FontManagerW fontManager; private SpreadsheetTableModelW tableModel; private SoundManagerW soundManager; protected DialogManager dialogManager = null; protected MaterialsManagerI fm; private Material activeMaterial; protected final ArticleElement articleElement; protected EuclidianPanelWAbstract euclidianViewPanel; protected Canvas canvas; private final GLookAndFeelI laf; protected ArrayList<Widget> popups = new ArrayList<Widget>(); private boolean justClosedPopup = false; // protected GeoGebraFrame frame = null; private GlobalKeyDispatcherW globalKeyDispatcher; // when losing focus, remembering it so that ENTER can give focus back private static volatile Element lastActiveElement = null; // but not in case of anything important in any app has focus, // we shall set it to true in each of those cases, e.g. AV input bar too !!! private static boolean anyAppHasFocus = true; /** * @param ae * {@link ArticleElement} * @param dimension * int * @param laf * (null for webSimple) {@link GLookAndFeelI} */ protected AppW(ArticleElement ae, int dimension, GLookAndFeelI laf) { super(getVersion(ae, dimension, laf)); setPrerelease(ae.getDataParamPrerelease()); // laf = null in webSimple setUndoRedoEnabled(ae.getDataParamEnableUndoRedo() && (laf == null || laf.undoRedoSupported())); if (ae.getDataParamPerspective().startsWith("exam")) { setNewExam(); ae.setAttribute("data-param-perspective", ""); } this.loc = new LocalizationW(dimension); this.articleElement = ae; ArticleElement.addNativeHandlers(articleElement, this); this.laf = laf; getTimerSystem(); this.showInputTop = InputPosition.algebraView; Window.addResizeHandler(new ResizeHandler() { @Override public void onResize(ResizeEvent event) { if (getArticleElement().getDataParamFitToScreen()) { AppW.this.getGgbApi().setSize(Window.getClientWidth(), Window.getClientHeight()); } windowResized(); } }); } private static Versions getVersion(ArticleElement ae, int dimension, GLookAndFeelI laf2) { return laf2 == null ? Versions.WEB_FOR_BROWSER_SIMPLE : laf2.getVersion(dimension, ae.getDataParamApp()); } protected final void windowResized() { for (MouseTouchGestureControllerW mtg : this.euclidianHandlers) { mtg.calculateEnvironment(); } if (this.getGuiManager() != null) { getGuiManager().setPixelRatio(getPixelRatio()); } if (!this.getAppletFrame().isHeaderPanelOpen()) { adjustViews(true); } } @Override public final void resetUniqueId() { uniqueId = UUIDW.randomUUID().toString(); setTubeId(0); } /** * @return id of local saved file */ public int getLocalID() { return this.localID; } /** * sets ID of local saved file * * @param id * int */ public void setLocalID(int id) { this.localID = id; } @Override public final DrawEquation getDrawEquation() { if (drawEquation == null) { drawEquation = new DrawEquationW(); } return drawEquation; } @Override public final SoundManager getSoundManager() { if (soundManager == null) { soundManager = new SoundManagerW(this); } return soundManager; } @Override public GgbAPIW getGgbApi() { if (ggbapi == null) { ggbapi = new GgbAPIW(this); } return ggbapi; } /** * @return {@link Canvas} */ public Canvas getCanvas() { return canvas; } @Override public final NormalizerMinimal getNormalizer() { if (normalizerMinimal == null) { normalizerMinimal = new NormalizerMinimal(); } return normalizerMinimal; } /** * inits factories */ protected void initFactories() { if (FormatFactory.getPrototype() == null) { FormatFactory.setPrototypeIfNull(new FormatFactoryW()); } if (AwtFactory.getPrototype() == null) { AwtFactory.setPrototypeIfNull(new AwtFactoryW()); } if (StringUtil.getPrototype() == null) { StringUtil.setPrototypeIfNull(new StringUtil()); } if (CASFactory.getPrototype() == null) { CASFactory.setPrototype((CASFactory) GWT.create(CASFactory.class)); } if (UtilFactory.getPrototype() == null) { UtilFactory.setPrototypeIfNull(new UtilFactoryW()); } } protected void afterCoreObjectsInited() { // TODO: abstract? } @Override final public GlobalKeyDispatcherW getGlobalKeyDispatcher() { if (globalKeyDispatcher == null) { globalKeyDispatcher = newGlobalKeyDispatcher(); if (articleElement != null && articleElement.getDataParamApp()) { globalKeyDispatcher.setFocused(true); } } return globalKeyDispatcher; } /** * @return a new instance of {@link GlobalKeyDispatcherW} */ private GlobalKeyDispatcherW newGlobalKeyDispatcher() { return new GlobalKeyDispatcherW(this); } @Override public EuclidianViewW getEuclidianView1() { return (EuclidianViewW) euclidianView; } private TimerSystemW timers; public TimerSystemW getTimerSystem() { if (timers == null) { timers = new TimerSystemW(this); } return timers; } @Override protected ScriptManager newScriptManager() { return new ScriptManagerW(this); } // ================================================ // native JS // ================================================ @Override public void callAppletJavaScript(String fun, Object[] args) { if (args == null || args.length == 0) { JsEval.callNativeJavaScript(fun); } else if (args.length == 1) { Log.debug("calling function: " + fun + "(" + args[0].toString() + ")"); JsEval.callNativeJavaScript(fun, args[0].toString()); } else { JsArrayString jsStrings = (JsArrayString) JavaScriptObject .createArray(); for (Object obj : args) { jsStrings.push(obj.toString()); } JsEval.callNativeJavaScriptMultiArg(fun, jsStrings); } } public void callAppletJavaScript(String fun, Object arg0, Object arg1) { if (arg0 == null && arg1 == null) { JsEval.callNativeJavaScript(fun); } else if (arg0 != null && arg1 == null) { // Log.debug("calling function: " + fun + "(" + arg0.toString() // + ")"); JsEval.callNativeJavaScript(fun, arg0.toString()); } else if (arg0 != null && arg1 != null) { JsEval.callNativeJavaScriptMultiArg(fun, arg0.toString(), arg1.toString()); } } private boolean toolLoadedFromStorage; private Storage storage; private ScriptLoadCallback scriptCallback; @Override public boolean loadXML(String xml) throws Exception { getXMLio().processXMLString(xml, true, false); return true; } @Override public MyXMLioW createXMLio(Construction cons) { return new MyXMLioW(cons.getKernel(), cons); } void doSetLanguage(String lang) { getLocalization().setLanguage(lang); // make sure digits are updated in all numbers getKernel().updateConstructionLanguage(); // update display & Input Bar Dictionary etc setLabels(); // inputField.setDictionary(getCommandDictionary()); notifyLocalizationLoaded(); } public void notifyLocalizationLoaded() { // TODO Auto-generated method stub } @Override public void setLanguage(final String browserLang) { final String lang = Language .getClosestGWTSupportedLanguage(browserLang); if (scriptCallback != null) { scriptCallback.cancel(); } if (lang != null && lang.equals(loc.getLocaleStr())) { Log.debug("Language is already " + loc.getLocaleStr()); setLabels(); notifyLocalizationLoaded(); return; } if (lang == null || "".equals(lang)) { Log.warn("language being set to empty string"); setLanguage("en"); return; } Log.debug("setting language to:" + lang + ", browser lang:" + browserLang); if (Browser.supportsSessionStorage() && LocalizationW.loadPropertiesFromStorage(lang, GeoGebraConstants.VERSION_STRING)) { doSetLanguage(lang); } else { // load keys (into a JavaScript <script> tag) DynamicScriptElement script = (DynamicScriptElement) Document.get() .createScriptElement(); script.setSrc(GWT.getModuleBaseURL() + "js/properties_keys_" + lang + ".js"); scriptCallback = new ScriptLoadCallback() { private boolean canceled = false; @Override public void onLoad() { if (canceled) { Log.debug("Async language file load canceled."); return; } // force reload doSetLanguage(lang); if (has(Feature.SWITCH_BETWEEN_KEYBOARD_LANGUAGE)) { updateKeyboardLanguage(); } if (Browser.supportsSessionStorage()) { LocalizationW.savePropertiesToStorage(lang, GeoGebraConstants.VERSION_STRING); } } @Override public void onError() { if (canceled) { Log.debug("Async language file load canceled."); return; } LocalizationW.loadPropertiesFromStorage(lang, ""); doSetLanguage(lang); } public void cancel() { canceled = true; } }; script.addLoadHandler(scriptCallback); Document.get().getBody().appendChild(script); } } void updateKeyboardLanguage() { getGuiManager().updateKeyboardLanguage(); } /** * @param language * language ISO code * @param country * country or country_variant */ public void setLanguage(String language, String country) { if (language == null || "".equals(language)) { Log.warn("error calling setLanguage(), setting to English (US): " + language + "_" + country); setLanguage("en"); return; } if (country == null || "".equals(country)) { setLanguage(language); return; } this.setLanguage(language + "_" + country); } @Override public LocalizationW getLocalization() { return loc; } /** * Translates localized command name into internal TODO check whether this * differs from translateCommand somehow and either document it or remove * this method * * @param cmd * localized command name * @return internal command name */ @Override final public String getInternalCommand(String cmd) { initTranslatedCommands(); String s; String cmdLower = StringUtil.toLowerCase(cmd); Commands[] values = Commands.values(); if (revTranslateCommandTable.isEmpty()) {// we should clear this cache // on language change! for (Commands c : values) {// and fill it now if needed s = Commands.englishToInternal(c).name(); // make sure that when si[] is typed in script, it's changed to // Si[] etc String lowerCaseCmd = StringUtil.toLowerCase(getLocalization() .getCommand(s)); revTranslateCommandTable.put(lowerCaseCmd, s); } } return revTranslateCommandTable.get(cmdLower); // return null; } HashMap<String, String> revTranslateCommandTable = new HashMap<String, String>(); @Override protected void fillCommandDict() { super.fillCommandDict(); revTranslateCommandTable.clear(); } /** * This method checks if the command is stored in the command properties * file as a key or a value. * * @param command * : a value that should be in the command properties files (part * of Internationalization) * @return the value "command" after verifying its existence. */ @Override final public String getReverseCommand(String command) { if (loc.getLanguage() == null) { // keys not loaded yet return command; } return super.getReverseCommand(command); } public void loadGgbFile(HashMap<String, String> archiveContent) throws Exception { AlgebraSettings algebraSettings = getSettings().getAlgebra(); algebraSettings.setModeChanged(false); loadFile(archiveContent); if (!algebraSettings.isModeChanged()) { algebraSettings.setTreeMode(SortMode.TYPE); } } /** * @param dataUrl * the data url to load the ggb file */ public void loadGgbFileAsBase64Again(String dataUrl) { prepareReloadGgbFile(); ViewW view = new ViewW(null, this); view.processBase64String(dataUrl); } public void loadGgbFileAsBinaryAgain(JavaScriptObject binary) { prepareReloadGgbFile(); ViewW view = new ViewW(null, this); view.processBinaryString(binary); } private void prepareReloadGgbFile() { getImageManager().reset(); } @SuppressWarnings("unchecked") private void loadFile(HashMap<String, String> archiveContent) throws Exception { beforeLoadFile(); HashMap<String, String> archive = (HashMap<String, String>) archiveContent .clone(); // Handling of construction and macro file String construction = archive.remove(MyXMLio.XML_FILE); String macros = archive.remove(MyXMLio.XML_FILE_MACRO); String defaults2d = archive.remove(MyXMLio.XML_FILE_DEFAULTS_2D); String defaults3d = null; if (is3D()) { defaults3d = archive.remove(MyXMLio.XML_FILE_DEFAULTS_3D); } String libraryJS = archive.remove(MyXMLio.JAVASCRIPT_FILE); // Construction (required) if (construction == null && macros == null) { throw new ConstructionException( "File is corrupt: No GeoGebra data found"); } if (construction != null) { // ggb file: remove all macros from kernel before processing kernel.removeAllMacros(); } // Macros (optional) // moved after the images are loaded, because otherwise // exception might come for macros which use images //if (macros != null) { // // macros = DataUtil.utf8Decode(macros); // // //DataUtil.utf8Decode(macros); // getXMLio().processXMLString(macros, true, true); //} // Library JavaScript (optional) if (libraryJS == null) { // TODO: && !isGGTfile) kernel.resetLibraryJavaScript(); } else { kernel.setLibraryJavaScript(libraryJS); } if (archive.entrySet() != null) { for (Entry<String, String> entry : archive.entrySet()) { maybeProcessImage(entry.getKey(), entry.getValue()); } } if (construction == null) { if (macros != null) { getXMLio().processXMLString(macros, true, true); } setCurrentFile(archiveContent); afterLoadFileAppOrNot(); if (!hasMacroToRestore()) { getGuiManager().refreshCustomToolsInToolBar(); } getGuiManager().updateToolbar(); return; } if (!getImageManager().hasImages()) { // Process Construction // construction = // DataUtil.utf8Decode(construction);//DataUtil.utf8Decode(construction); // Before opening the file, // hide navigation bar for construction steps if visible. // (Don't do this for ggt files.) setHideConstructionProtocolNavigation(); // getKernel().setNotifyViewsActive(false); TODO would make things a // lot faster, but problems with construction step and AV ordering if (macros != null) { // Log.debug("start processing macros: // "+System.currentTimeMillis()); getXMLio().processXMLString(macros, true, true); // Log.debug("end processing macros: // "+System.currentTimeMillis()); } // Log.debug("start processing" + System.currentTimeMillis()); getXMLio().processXMLString(construction, true, false); // Log.debug("end processing" + System.currentTimeMillis()); // defaults (optional) if (defaults2d != null) { getXMLio().processXMLString(defaults2d, false, true); } if (defaults3d != null) { getXMLio().processXMLString(defaults3d, false, true); } setCurrentFile(archiveContent); afterLoadFileAppOrNot(); // getKernel().setNotifyViewsActive(true); } else { // on images do nothing here: wait for callback when images loaded. getImageManager().triggerImageLoading( /* DataUtil.utf8Decode( */construction/* * )/*DataUtil.utf8Decode * (construction) */, defaults2d, defaults3d, macros, getXMLio(), this); setCurrentFile(archiveContent); } } /** * Sets viewId for each macro * * @param toolbar3D * toolbar string for 3D View that includes its macros as well. */ public void setMacroViewIds(String toolbar3D) { int macroCount = kernel.getMacroNumber(); for (int i = 0; i < macroCount; i++) { Macro macro = kernel.getMacro(i); if (macro.getViewId() == null) { int macroMode = EuclidianConstants.MACRO_MODE_ID_OFFSET + i; Log.debug("[MM] " + macro); if (toolbar3D.contains(String.valueOf(macroMode))) { macro.setViewId(VIEW_EUCLIDIAN3D); } else { macro.setViewId(VIEW_EUCLIDIAN); } } } } /** * Prepare for loading file */ public void beforeLoadFile() { startCollectingRepaints(); // make sure the image manager will not wait for images from the *old* // file if (this.getImageManager() != null) { this.getImageManager().reset(); } getEuclidianView1().setReIniting(true); if (hasEuclidianView2EitherShowingOrNot(1)) { getEuclidianView2(1).setReIniting(true); } } @SuppressWarnings("unchecked") @Override public void setCurrentFile(Object file) { if (currentFile == file) { return; } currentFile = (HashMap<String, String>) file; // if (!isIniting() && isUsingFullGui()) { // updateTitle(); // getGuiManager().updateMenuWindow(); // } } /** * @return current .zip file as hashmap */ public HashMap<String, String> getCurrentFile() { return currentFile; } @Override public void reset() { if (currentFile != null) { try { loadGgbFile(currentFile); } catch (Exception e) { clearConstruction(); } } else { clearConstruction(); } } private void maybeProcessImage(String filename0, String content) { String fn = filename0.toLowerCase(); if (fn.equals(MyXMLio.XML_FILE_THUMBNAIL)) { return; // Ignore thumbnail } FileExtensions ext = StringUtil.getFileExtension(fn); // Ignore non image files if (!ext.isImage()) { return; } String filename = filename0; // bug in old versions (PNG saved with wrong extension) // change BMP, TIFF, TIF -> PNG if (!ext.isAllowedImage()) { filename = StringUtil.changeFileExtension(filename, FileExtensions.PNG); } // for file names e.g. /geogebra/main/nav_play.png in GeoButtons // Log.debug("filename2 = " + filename); // Log.debug("ext2 = " + ext); if (ext.equals(FileExtensions.SVG)) { // IE11/12 seems to require SVG to be base64 encoded addExternalImage(filename, "data:image/svg+xml;base64," + Browser.encodeBase64(content)); } else { addExternalImage(filename, content); } } public void addExternalImage(String filename, String src) { getImageManager().addExternalImage(filename, src); } @Override public final ImageManagerW getImageManager() { return imageManager; } protected void initImageManager() { imageManager = new ImageManagerW(); } @Override public final void setXML(String xml, boolean clearAll) { if (clearAll) { setCurrentFile(null); } try { // make sure objects are displayed in the correct View setActiveView(App.VIEW_EUCLIDIAN); getXMLio().processXMLString(xml, clearAll, false); } catch (MyError err) { err.printStackTrace(); showError(err); } catch (Exception e) { e.printStackTrace(); showError("LoadFileFailed"); } } @Override public boolean clearConstruction() { // if (isSaved() || saveCurrentFile()) { kernel.clearConstruction(true); kernel.initUndoInfo(); resetMaxLayerUsed(); setCurrentFile(null); setMoveMode(); return true; // } // return false; } @Override public final MyImage getExternalImageAdapter(String fileName, int width, int height) { ImageElement im = getImageManager().getExternalImage(fileName, this); if (im == null) { return null; } if (width != 0 && height != 0) { im.setWidth(width); im.setHeight(height); } return new MyImageW(im, fileName.toLowerCase().endsWith(".svg")); } @Override public final UndoManager getUndoManager(Construction cons) { return new UndoManagerW(cons); } @Override public final GeoElementGraphicsAdapter newGeoElementGraphicsAdapter() { return new GeoElementGraphicsAdapterW(this); } @Override public final void runScripts(final GeoElement geo1, final String string) { invokeLater(new Runnable() { @Override public void run() { geo1.runClickScripts(string); } }); } @Override public void invokeLater(final Runnable runnable) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runnable.run(); } }); } @Override public final CASFactory getCASFactory() { return CASFactory.getPrototype(); } @Override public void fileNew() { // clear all // triggers the "do you want to save" dialog // so must be called first if (!clearConstruction()) { return; } kernel.getInputPreviewHelper().clear(); clearInputBar(); resetUniqueId(); setLocalID(-1); resetActiveMaterial(); if (isWhiteboardActive()) { // reset pen default size getEuclidianController().getPen().DEFAULT_PEN_LINE .setLineThickness(EuclidianConstants.DEFAULT_PEN_SIZE); // reset eraser default size getActiveEuclidianView().getSettings() .setDeleteToolSize(EuclidianConstants.DEFAULT_ERASER_SIZE); } if (getGoogleDriveOperation() != null) { getGoogleDriveOperation().resetStorageInfo(); } adjustViews(false); } /** * @param macro * Macro need to be stored. * @param writeBack * Is it a new one or a modification. */ public void storeMacro(Macro macro, boolean writeBack) { createStorage(); if (storage == null) { return; } String b64 = getGgbApi().getMacrosBase64(); storage.setItem(STORAGE_MACRO_ARCHIVE, b64); storage.setItem(STORAGE_MACRO_KEY, macro.getToolName()); if (writeBack) { return; } // Storage.addStorageEventHandler(new StorageEvent.Handler() { // // public void onStorageChange(StorageEvent event) { // if (STORAGE_MACRO_KEY.equals(event.getKey())) { // Log.debug("[STORAGE] '" + STORAGE_MACRO_KEY // + "' has changed."); // } // } // }); } protected void createStorage() { if (storage == null) { storage = Storage.getSessionStorageIfSupported(); } } protected boolean hasMacroToRestore() { createStorage(); if (storage != null) { StorageMap map = new StorageMap(storage); if (map.containsKey(STORAGE_MACRO_ARCHIVE)) { return true; } } return false; } protected void restoreMacro() { createStorage(); if (storage != null) { StorageMap map = new StorageMap(storage); if (map.containsKey(STORAGE_MACRO_ARCHIVE)) { getKernel().removeAllMacros(); String b64 = storage.getItem(STORAGE_MACRO_ARCHIVE); getGgbApi().setBase64(b64); } } } protected boolean openMacroFromStorage() { createStorage(); if (storage != null) { StorageMap map = new StorageMap(storage); if (map.containsKey(STORAGE_MACRO_KEY)) { String macroName = storage.getItem(STORAGE_MACRO_KEY); try { // Log.debug("[STORAGE] restoring macro " + macroName); openMacro(macroName); Window.setTitle(macroName); setToolLoadedFromStorage(true); return true; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return false; } /** * sets the timestamp of last synchronization with ggbTube * * @param syncStamp * long */ public void setSyncStamp(long syncStamp) { this.syncStamp = syncStamp; } /** * @return timestamp of last synchronization with ggbTube */ public long getSyncStamp() { return this.syncStamp; } /** * @return GoogleDriveOperation */ public GoogleDriveOperation getGoogleDriveOperation() { return googleDriveOperation; } public boolean openFile(JavaScriptObject fileToHandle, JavaScriptObject callback) { if (getArticleElement() != null) { getArticleElement().setAttribute("data-param-perspective", ""); } return doOpenFile(fileToHandle, callback); } /** * Opens the ggb or ggt file * * @param fileToHandle * @param callback * @return returns true, if fileToHandle is ggb or ggt file, otherwise * returns false. Note that If the function returns true, it's don't * mean, that the file opening was successful, and the opening * finished already. */ public native boolean doOpenFile(JavaScriptObject fileToHandle, JavaScriptObject callback) /*-{ var ggbRegEx = /\.(ggb|ggt|csv|off)$/i; if (!fileToHandle.name.toLowerCase().match(ggbRegEx)) return false; var appl = this; var reader = new FileReader(); reader.onloadend = function(ev) { if (reader.readyState === reader.DONE) { var fileStr = reader.result; if (fileToHandle.name.toLowerCase().match(/\.(ggb|ggt)$/i)) { appl.@org.geogebra.web.html5.main.AppW::loadGgbFileAsBase64Again(Ljava/lang/String;)(fileStr); } if (fileToHandle.name.toLowerCase().match(/\.(csv)$/i)) { appl.@org.geogebra.web.html5.main.AppW::openCSV(Ljava/lang/String;)(atob(fileStr.substring(fileStr.indexOf(",")+1))); } if (fileToHandle.name.toLowerCase().match(/\.(off)$/i)) { appl.@org.geogebra.web.html5.main.AppW::openOFF(Ljava/lang/String;)(atob(fileStr.substring(fileStr.indexOf(",")+1))); } if (callback != null) callback(); } }; reader.readAsDataURL(fileToHandle); return true; }-*/; /** * NEVER CALLED?? */ public void addFileLoadListener(FileLoadListener f) { this.fileLoadListeners.add(f); } private ArrayList<FileLoadListener> fileLoadListeners = new ArrayList<FileLoadListener>(); /** * Notify listeners about loaded file, see * {@link #addFileLoadListener(FileLoadListener)} */ public final void notifyFileLoaded() { for (FileLoadListener listener : fileLoadListeners) { listener.onFileLoad(); } } @Override public double getMillisecondTime() { return GeoGebraProfilerW.getMillisecondTimeNative(); } // public native void copyBase64NonWebApp(String str) /*-{ // var userAgent = $wnd.navigator.userAgent.toLowerCase(); // if ((userAgent.indexOf('msie') > -1) // || (userAgent.indexOf('trident') > -1)) { // // It is a good question what shall we do in Internet Explorer? // // Security settings may block clipboard, new browser tabs, // window.prompt, alert // // Use a custom alert! but this does not seem to work either // // //this.@org.geogebra.web.html5.main.GlobalKeyDispatcherW::showConfirmDialog(Ljava/lang/String;)(str); // // alternative, better than nothing, but not always working // //if ($wnd.clipboardData) { // // $wnd.clipboardData.setData('Text', str); // //} // // // then just do the same as in other cases, for now // if ($wnd.prompt) { // $wnd.prompt('Base64', str); // } else { // this.@org.geogebra.web.html5.main.AppW::showConfirmDialog(Ljava/lang/String;Ljava/lang/String;)("Base64", // str); // } // } else { // // otherwise, we should do the following: // if ($wnd.prompt) { // $wnd.prompt('Base64', str); // } else { // this.@org.geogebra.web.html5.main.AppW::showConfirmDialog(Ljava/lang/String;Ljava/lang/String;)("Base64", // str); // } // } // }-*/; /** * @param str * string to copy */ public void copyBase64ToClipboardChromeWebAppCase(String str) { // This should do nothing in webSimple! } /** * @param title * confirmation dialog title * @param mess * message */ public void showConfirmDialog(String title, String mess) { // This should do nothing in webSimple! } /** * @param id * material ID * @param onError * callback for errors */ public void openMaterial(String id, Runnable onError) { // only with GUI } private NetworkOperation networkOperation; /* * True if showing the "alpha" in Input Boxes is allowed. (we can hide the * symbol buttons with data-param-allowSymbolTable parameter) */ private boolean allowSymbolTables = true; /** * @return OfflineOperation event flow */ public NetworkOperation getNetworkOperation() { return networkOperation; } /** * Initialize online/offline state listener */ protected void initNetworkEventFlow() { Network network = new Network() { private native boolean checkOnlineState() /*-{ return $wnd.navigator.onLine; }-*/; @Override public boolean onLine() { return checkOnlineState(); } }; NativeEventAttacher attacher = new NativeEventAttacher() { private native void nativeAttach(String t, BaseEventPool ep) /*-{ $wnd.addEventListener(t, function() { ep.@org.geogebra.common.move.events.BaseEventPool::trigger()(); }); $doc.addEventListener(t, function() { ep.@org.geogebra.common.move.events.BaseEventPool::trigger()(); }); }-*/; @Override public void attach(String type, BaseEventPool eventPool) { nativeAttach(type, eventPool); } }; networkOperation = new NetworkOperation(network); BaseEventPool offlineEventPool = new BaseEventPool(networkOperation, false); attacher.attach("offline", offlineEventPool); BaseEventPool onlineEventPool = new BaseEventPool(networkOperation, true); attacher.attach("online", onlineEventPool); OfflineView ov = new OfflineView(); networkOperation.setView(ov); } public void setAllowSymbolTables(boolean allowST) { allowSymbolTables = allowST; } /** * @return true, if alpha buttons may be visible in input boxes. */ public boolean isAllowedSymbolTables() { return allowSymbolTables; } private boolean allowStyleBar = true; /** * @param flag * whether stylebar can be shown also when menubar is hidden */ public void setAllowStyleBar(boolean flag) { allowStyleBar = flag; } /** * @return whether it's allowed to show stylebar even if menubar closed */ public boolean isStyleBarAllowed() { return allowStyleBar; } @Override public AlgoKimberlingWeightsInterface getAlgoKimberlingWeights() { if (getKimberlingw() != null) { return getKimberlingw(); } GWT.runAsync(new RunAsyncCallback() { @Override public void onSuccess() { setKimberlingw(new AlgoKimberlingWeights()); setKimberlingWeightFunction(getKimberlingw()); getKernel().updateConstruction(); } @Override public void onFailure(Throwable reason) { Log.warn("AlgoKimberlingWeights loading failure"); } }); return getKimberlingw(); } public native void setKimberlingWeightFunction( AlgoKimberlingWeightsInterface kimberlingw) /*-{ $wnd.geogebraKimberlingWeight = function(obj) { return kimberlingw.@org.geogebra.common.main.AlgoKimberlingWeightsInterface::weight(Lorg/geogebra/common/main/AlgoKimberlingWeightsParams;)(obj); } }-*/; @Override public native double kimberlingWeight(AlgoKimberlingWeightsParams kparams) /*-{ if ($wnd.geogebraKimberlingWeight) { return $wnd.geogebraKimberlingWeight(kparams); } // should not execute! return 0; }-*/; @Override public AlgoCubicSwitchInterface getAlgoCubicSwitch() { if (getCubicw() != null) { return getCubicw(); } GWT.runAsync(new RunAsyncCallback() { @Override public void onSuccess() { setCubicw(new AlgoCubicSwitch()); setCubicSwitchFunction(getCubicw()); getKernel().updateConstruction(); } @Override public void onFailure(Throwable reason) { Log.debug("AlgoKimberlingWeights loading failure"); } }); return getCubicw(); } public native void setCubicSwitchFunction(AlgoCubicSwitchInterface cubicw) /*-{ $wnd.geogebraCubicSwitch = function(obj) { return cubicw.@org.geogebra.common.main.AlgoCubicSwitchInterface::getEquation(Lorg/geogebra/common/main/AlgoCubicSwitchParams;)(obj); } }-*/; @Override public native String cubicSwitch(AlgoCubicSwitchParams kparams) /*-{ if ($wnd.geogebraCubicSwitch) { return $wnd.geogebraCubicSwitch(kparams); } // should not execute! return 0; }-*/; @Override public CommandDispatcher getCommandDispatcher(Kernel k) { CommandDispatcher cmd = new CommandDispatcherW(k); if (!enableGraphing()) { cmd.setEnabled(false); } return cmd; } /** * @param viewId * @return the plotpanel euclidianview */ public EuclidianViewW getPlotPanelEuclidianView(int viewId) { if (getGuiManager() == null) { return null; } return (EuclidianViewW) getGuiManager().getPlotPanelView(viewId); } public boolean isPlotPanelEuclidianView(int viewID) { if (getGuiManager() == null) { return false; } return getGuiManager().getPlotPanelView(viewID) != null; } public void imageDropHappened(String imgFileName, String fileStr, String fileStr2, GeoPoint loc1) { imageDropHappened(imgFileName, fileStr, fileStr2, loc1, 0, 0); } /** * Loads an image and puts it on the canvas (this happens on webcam input) * On drag&drop or insert from URL this would be called too, but that would * set security exceptions * * @param url * - the data url of the image * @param clientx * - desired position on the canvas (x) - unused * @param clienty * - desired position on the canvas (y) - unused */ public void urlDropHappened(String url, int clientx, int clienty) { // Filename is temporarily set until a better solution is found // TODO: image file name should be reset after the file data is // available MD5EncrypterGWTImpl md5e = new MD5EncrypterGWTImpl(); String zip_directory = md5e.encrypt(url); // with dummy extension, maybe gif or jpg in real String imgFileName = zip_directory + ".png"; String fn = imgFileName; int index = imgFileName.lastIndexOf('/'); if (index != -1) { fn = fn.substring(index + 1, fn.length()); // filename without } // path fn = org.geogebra.common.util.Util.processFilename(fn); // filename will be of form // "a04c62e6a065b47476607ac815d022cc\liar.gif" imgFileName = zip_directory + '/' + fn; doDropHappened(imgFileName, url, null, 0, 0); if (insertImageCallback != null) { this.insertImageCallback.run(); } } /** * Loads an image and puts it on the canvas (this happens by drag & drop) * * @param imgFileName * - the file name of the image * @param fileStr * - the image data url * @param notUsed * - not used * @param loc */ public void imageDropHappened(String imgFileName, String fileStr, String notUsed, GeoPoint loc1, int width, int height) { MD5EncrypterGWTImpl md5e = new MD5EncrypterGWTImpl(); String zip_directory = md5e.encrypt(fileStr); String fn = imgFileName; int index = imgFileName.lastIndexOf('/'); if (index != -1) { fn = fn.substring(index + 1, fn.length()); // filename without } // path fn = org.geogebra.common.util.Util.processFilename(fn); // filename will be of form // "a04c62e6a065b47476607ac815d022cc\liar.gif" fn = zip_directory + '/' + fn; doDropHappened(fn, fileStr, loc1, width, height); } /** * @param pt * drop location TODO make sure it's used */ private void doDropHappened(String imgFileName, String fileStr, GeoPoint pt, int width, int height) { Construction cons = getKernel().getConstruction(); getImageManager().addExternalImage(imgFileName, fileStr); GeoImage geoImage = new GeoImage(cons); getImageManager().triggerSingleImageLoading(imgFileName, geoImage); geoImage.setImageFileName(imgFileName, width, height); getGuiManager().setImageCornersFromSelection(geoImage); if (has(Feature.AV_PLUS) && getImageManager().isPreventAuxImage()) { geoImage.setAuxiliaryObject(false); } setDefaultCursor(); } /** * Opens the image file * * @param fileToHandle * javascript handle for the file * @param callback * load callback * @return returns true, if fileToHandle image file, otherwise return false. * Note that If the function returns true, it's don't mean, that the * file opening was successful, and the opening finished already. */ public native boolean openFileAsImage(JavaScriptObject fileToHandle, JavaScriptObject callback) /*-{ var imageRegEx = /\.(png|jpg|jpeg|gif|bmp)$/i; if (!fileToHandle.name.toLowerCase().match(imageRegEx)) return false; var appl = this; var reader = new FileReader(); reader.onloadend = function(ev) { if (reader.readyState === reader.DONE) { var fileStr = reader.result; var fileName = fileToHandle.name; appl.@org.geogebra.web.html5.main.AppW::imageDropHappened(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/geogebra/common/kernel/geos/GeoPoint;)(fileName, fileStr, fileStr, null); if (callback != null) { callback(); } } }; reader.readAsDataURL(fileToHandle); return true; }-*/; /** * @return the id of the articleelement */ public String getArticleId() { return articleElement.getId(); } /** * @param articleid * the article id added by scriptManager * * this method is called by scriptmanager after ggbOnInit */ public static native void appletOnLoad(String articleid) /*-{ if (typeof $wnd.ggbAppletOnLoad === "function") { $wnd.ggbAppletOnLoad(articleid); } }-*/; protected GOptionPaneW getOptionPane() { return getGuiManager() != null ? getGuiManager().getOptionPane() : new GOptionPaneW(getPanel()); } @Override public void setActiveView(int evID) { if (getGuiManager() != null) { getGuiManager().setActiveView(evID); } } public final ClientInfo getClientInfo() { ClientInfo clientInfo = new ClientInfo(); clientInfo.setModel(getLoginOperation().getModel()); clientInfo.setLanguage(getLocalization().getLanguage()); clientInfo.setWidth((int) getWidth()); clientInfo.setHeight((int) getHeight()); clientInfo.setType(getClientType()); clientInfo.setId(getClientID()); return clientInfo; } /** * Initializes the user authentication */ public void initSignInEventFlow(LogInOperation op, boolean mayLogIn) { // Initialize the signIn operation loginOperation = op; if (getNetworkOperation().isOnline()) { if (this.getLAF() != null && this.getLAF().supportsGoogleDrive()) { initGoogleDriveEventFlow(); } if (mayLogIn) { loginOperation.performTokenLogin(); } } else { loginOperation.startOffline(); } } protected void initGoogleDriveEventFlow() { // overriden in AppW } private ArrayList<ViewsChangedListener> viewsChangedListener = new ArrayList<ViewsChangedListener>(); private GDimension preferredSize; public void addViewsChangedListener(ViewsChangedListener l) { viewsChangedListener.add(l); } public void fireViewsChangedEvent() { for (ViewsChangedListener l : viewsChangedListener) { l.onViewsChanged(); } } @Override public FontManager getFontManager() { return fontManager; } /** * Initializes the application, seeds factory prototypes, creates Kernel and * MyXMLIO * */ protected void initCommonObjects() { initFactories(); // Online - Offline event handling begins here initNetworkEventFlow(); } /** * * @param this_app * application * @return a kernel */ protected Kernel newKernel(App this_app) { return new Kernel(this_app, new GeoFactory()); } /** * Initializes Kernel, EuclidianView, EuclidianSettings, etc.. * * @param undoActive * whether undo manager should be initialized * @param this_app * app for creating kernel */ protected void initCoreObjects(final App this_app) { kernel = newKernel(this_app); // init settings settings = companion.newSettings(); // if (has(Feature.AV_EXTENSIONS)) { // settings.getAlgebra().setTreeMode(SortMode.ORDER.ordinal()); // } fontManager = new FontManagerW(); setFontSize(16, false); initEuclidianViews(); initImageManager(); setFontSize(16, true); // setLabelDragsEnabled(false); getScriptManager();// .ggbOnInit();//this is not called here because we // have to delay it // until the canvas is first drawn FileDropHandlerW.registerDropHandler(getFrameElement(), this); setViewsEnabled(); } private void setViewsEnabled() { if (getArticleElement().getDataParamEnableCAS(false) || !getArticleElement().getDataParamEnableCAS(true)) { getSettings().getCasSettings().setEnabled( getArticleElement().getDataParamEnableCAS(false)); } if (getArticleElement().getDataParamEnable3D(false) || !getArticleElement().getDataParamEnable3D(true)) { getSettings().getEuclidian(-1).setEnabled( getArticleElement().getDataParamEnable3D(false)); } if (getArticleElement().getDataParamEnableGraphing(false) || !getArticleElement().getDataParamEnableGraphing(true)) { boolean enableGraphing = getArticleElement() .getDataParamEnableGraphing(false); getSettings().getEuclidian(1).setEnabled(enableGraphing); getSettings().getEuclidian(2).setEnabled(enableGraphing); kernel.getAlgebraProcessor().setCommandsEnabled(enableGraphing); } } /** * @return preferred size */ public GDimension getPreferredSize() { if (preferredSize == null) { return new GDimensionW(800, 600); } return preferredSize; } @Override public void setPreferredSize(GDimension size) { preferredSize = size; } /** * @return element of the AppFrame / GeoGebraFrame */ public Element getFrameElement() { // Log.debug("getFrameElement() returns null, should be overridden by // subclasses"); return null; } @Override public void setWaitCursor() { if (getDialogManager() instanceof LoadingApplication) { ((LoadingApplication) getDialogManager()).showLoadingAnimation(); } RootPanel.get().addStyleName("cursor_wait"); } @Override public void setDefaultCursor() { if (getDialogManager() instanceof LoadingApplication) { ((LoadingApplication) getDialogManager()).hideLoadingAnimation(); } resetCursor(); } /** * Set default cursor for whole body */ public void resetCursor() { RootPanel.get().removeStyleName("cursor_wait"); } private void updateContentPane(boolean updateComponentTreeUI) { if (initing) { return; } addMacroCommands(); // used in AppWapplet buildApplicationPanel(); // update sizes euclidianView.updateSize(); // needed: GGB-624 if (hasEuclidianView2(1)) { getEuclidianView2(1).updateSize(); } // update layout if (updateComponentTreeUI) { updateTreeUI(); } // reset mode and focus set1rstMode(); if (euclidianView.isShowing()) { requestFocusInWindow(); } } /** * Updates the GUI of the main component. */ public void updateContentPane() { updateContentPane(true); } protected void requestFocusInWindow() { if (!articleElement.preventFocus()) { euclidianView.requestFocusInWindow(); } } /** * @return current look and feel */ public GLookAndFeelI getLAF() { return laf; } @Override public SpreadsheetTableModel getSpreadsheetTableModel() { if (tableModel == null) { tableModel = new SpreadsheetTableModelW(this, SPREADSHEET_INI_ROWS, SPREADSHEET_INI_COLS); } return tableModel; } protected abstract void updateTreeUI(); public void buildApplicationPanel() { // overridden in AppWApplet } public void appSplashCanNowHide() { // only with GUI } /** * Called from GuiManager, implementation depends on subclass * * @return toolbar object */ public ToolBarInterface getToolbar() { return null; } // methods used just from AppWapplet (and AppWsimple) /** * * @param w * last selected view * @param el * target element */ public void focusLost(View w, Element el) { // other things are handled in subclasses of AppW // anyAppHasFocus = false; if (el != null) { setLastActive(el); } } /** * * @param w * selected view * @param el * target element */ public void focusGained(View w, Element el) { // this is used through the super keyword // anyAppHasFocus = true; if (el != null) { setLastActive(el); } } public void setCustomToolBar() { // only needed in AppWFull } /** * @return whether EV1 is the only visible view */ public boolean onlyGraphicsViewShowing() { if (!isUsingFullGui() || getGuiManager() == null) { return true; } return getGuiManager().getLayout().isOnlyVisible(App.VIEW_EUCLIDIAN); } @Override public boolean isUsingFullGui() { return useFullGui; } @Override public GuiManagerInterfaceW getGuiManager() { return null; } // ======================================================== // Getters/Setters // ======================================================== @Override public boolean isHTML5Applet() { return true; } public ArticleElement getArticleElement() { return articleElement; } @Override public boolean isApplet() { return !getArticleElement().getDataParamApp(); } public Material getActiveMaterial() { return this.activeMaterial; } public void setActiveMaterial(Material mat) { this.activeMaterial = mat; } private void resetActiveMaterial() { this.activeMaterial = null; } @Override protected EuclidianView newEuclidianView(boolean[] showEvAxes, boolean showEvGrid) { return euclidianView = newEuclidianView(euclidianViewPanel, getEuclidianController(), showEvAxes, showEvGrid, 1, getSettings().getEuclidian(1)); } /** * * @param evPanel * @param ec * @param showEvAxes * @param showEvGrid * @param id * @param settings * view settings * @return new euclidian view */ public EuclidianViewW newEuclidianView(EuclidianPanelWAbstract evPanel, EuclidianController ec, boolean[] showEvAxes, boolean showEvGrid, int id, EuclidianSettings evSettings) { return new EuclidianViewW(evPanel, ec, id, evSettings); } @Override public EuclidianController newEuclidianController(Kernel kernel1) { return new EuclidianControllerW(kernel1); } @Override public DialogManager getDialogManager() { return dialogManager; } @Override public Factory getFactory() { Factory ret = Factory.getPrototype(); if (ret == null) { ret = new FactoryW(); Factory.setPrototype(ret); } return ret; } public void restoreCurrentUndoInfo() { if (isUndoActive()) { kernel.restoreCurrentUndoInfo(); // isSaved = false; } } // =================================================== // Views // =================================================== public EuclidianPanelWAbstract getEuclidianViewpanel() { return euclidianViewPanel; } @Override public boolean hasEuclidianView2EitherShowingOrNot(int idx) { return (getGuiManager() != null) && getGuiManager().hasEuclidianView2EitherShowingOrNot(idx); } @Override public EuclidianViewW getEuclidianView2(int idx) { if (getGuiManager() == null) { return null; } return (EuclidianViewW) getGuiManager().getEuclidianView2(idx); } @Override public EuclidianView getActiveEuclidianView() { if (getGuiManager() == null) { return getEuclidianView1(); } return getGuiManager().getActiveEuclidianView(); } @Override public boolean isShowingEuclidianView2(int idx) { return (getGuiManager() != null) && getGuiManager().hasEuclidianView2(idx) && getGuiManager().getEuclidianView2(idx).isShowing(); } @Override public EuclidianViewW createEuclidianView() { return (EuclidianViewW) this.euclidianView; } @Override public AlgebraView getAlgebraView() { if (getGuiManager() == null) { return null; } return getGuiManager().getAlgebraView(); } @Override public boolean showView(int view) { if (getGuiManager() == null) { return (view == App.VIEW_EUCLIDIAN); } return getGuiManager().showView(view); } // ======================================================== // Languages // ======================================================== /** * Checks for GeoGebraLangUI in URL, then in cookie, then checks browser * language */ public String getLanguageFromCookie() { String lCookieValue = Location.getParameter("GeoGebraLangUI"); if (StringUtil.empty(lCookieValue)) { lCookieValue = Cookies.getCookie("GeoGebraLangUI"); } Storage localStorage = Storage.getLocalStorageIfSupported(); if (StringUtil.empty(lCookieValue) && localStorage != null) { lCookieValue = localStorage.getItem("GeoGebraLangUI"); } if (StringUtil.empty(lCookieValue)) { lCookieValue = Browser.navigatorLanguage(); } return lCookieValue; } @Override public void setLabels() { if (initing) { return; } if (getGuiManager() != null) { getGuiManager().setLabels(); } // if (rbplain != null) { kernel.updateLocalAxesNames(); kernel.setViewsLabels(); // } updateCommandDictionary(); } @Override public boolean letRedefine() { // AbstractApplication.debug("implementation needed"); // TODO // Auto-generated return true; } // ============================================ // IMAGES // ============================================ private static String createImageSrc(String ext, String base64) { String dataUrl = "data:image/" + ext + ";base64," + base64; return dataUrl; } public ImageElement getRefreshViewImage() { ImageElement imgE = ImageManagerW.getInternalImage( GuiResourcesSimple.INSTANCE.viewRefresh()); attachNativeLoadHandler(imgE); return imgE; } public ImageElement getPlayImage() { return ImageManagerW.getInternalImage( GuiResourcesSimple.INSTANCE.icons_play_circle()); } public ImageElement getPauseImage() { return ImageManagerW.getInternalImage( GuiResourcesSimple.INSTANCE.icons_play_pause_circle()); } public ImageElement getPlayImageHover() { return ImageManagerW.getInternalImage( GuiResourcesSimple.INSTANCE.icons_play_circle_hover()); } public ImageElement getPauseImageHover() { return ImageManagerW.getInternalImage( GuiResourcesSimple.INSTANCE.icons_play_pause_circle_hover()); } // ============================================ // XML // ============================================ @Override protected int getWindowWidth() { if (getWidth() > 0) { return (int) getWidth(); } return 800; } @Override protected int getWindowHeight() { if (getHeight() > 0) { return (int) getHeight(); } return 600; } @Override protected void getLayoutXML(StringBuilder sb, boolean asPreference) { if (getGuiManager() == null) { initGuiManager(); } if (getGuiManager() != null) { getGuiManager().getLayout().getXml(sb, asPreference); } } // ============================================ // FONTS // ============================================ @Override public GFont getPlainFontCommon() { return new GFontW("normal"); } // ============================================ // CURSORS // ============================================ @Override public void updateUI() { if (getGuiManager() != null) { getGuiManager().setPixelRatio(getPixelRatio()); } // Log.debug("updateUI: implementation needed for GUI"); // TODO } // ======================================== // EXPORT & GEOTUBE // ======================================== public final void copyEVtoClipboard(EuclidianViewW ev) { String image = ev.getExportImageDataUrl(3, true); String title = ev.getApplication().getKernel().getConstruction() .getTitle(); title = "".equals(title) ? "GeoGebraImage" : title; getFileManager().exportImage(image, title, "png"); } @Override public void copyGraphicsViewToClipboard() { Log.debug("unimplemented"); } // ======================================== // MISC // ======================================== @Override public GeoElementSelectionListener getCurrentSelectionListener() { // TODO Auto-generated method stub return null; } @Override public void showURLinBrowser(final String pageUrl) { getFileManager().open(pageUrl, "_blank", ""); Log.debug("opening: " + pageUrl); // assume showURLinBrowserWaiterFixedDelay is called before } @Override public void initGuiManager() { // this should not be called from AppWsimple! // this method should be overridden in // AppWapplet and AppWapplication! } @Override public void exitAll() { Log.debug("unimplemented"); } /** * This is used for LaTeXes in GeoGebraWeb (DrawText, DrawEquationWeb) */ @Override public void scheduleUpdateConstruction() { // set up a scheduler in case 0.5 seconds would not be enough for the // computer Scheduler.get().scheduleDeferred(sucCallback); } Timer timeruc = new Timer() { @Override public void run() { updateConsBoundingBox(); } }; Scheduler.ScheduledCommand sucCallback = new Scheduler.ScheduledCommand() { @Override public void execute() { // 0.5 seconds is good for the user and maybe for the computer // too timeruc.schedule(500); } }; private Runnable closeBroserCallback; private Runnable insertImageCallback; protected void updateConsBoundingBox() { boolean force = kernel.getForceUpdatingBoundingBox(); kernel.setForceUpdatingBoundingBox(true); kernel.getConstruction().updateConstructionLaTeX(); kernel.notifyRepaint(); kernel.setForceUpdatingBoundingBox(force); } public String getDataParamId() { return getArticleElement().getDataParamId(); } public abstract void afterLoadFileAppOrNot(); /** * Returns the tool name and tool help text for the given tool as an HTML * text that is useful for tooltips. * * @param mode * : tool ID */ @Override public String getToolTooltipHTML(int mode) { String toolTipHtml = super.getToolTooltipHTML(mode); return toolTipHtml; } public void recalculateEnvironments() { if (getGuiManager() != null) { getGuiManager().recalculateEnvironments(); } if (getEuclidianView1() != null) { getEuclidianView1().getEuclidianController().calculateEnvironment(); } } @Override public void updateViewSizes() { // TODO Auto-generated method stub } public void registerPopup(Widget widget) { popups.add(widget); } public void centerAndResizePopups() { // to be overridden in AppWFull } @Override public void closePopups() { closePopupsNoTooltips(); ToolTipManagerW.hideAllToolTips(); if (getGuiManager() != null && getGuiManager().hasAlgebraView()) { getAlgebraView().resetItems(false); } if (getGuiManager() != null && getGuiManager().hasCasView()) { getGuiManager().getCasView().resetItems(false); } if (getActiveEuclidianView() != null) { getActiveEuclidianView().getEuclidianController() .setObjectMenuActive(false); } } public void closePopupsNoTooltips() { justClosedPopup = false; for (Widget widget : popups) { justClosedPopup = true; widget.setVisible(false); } popups.clear(); } public void addAsAutoHidePartnerForPopups(Element el) { for (int i = 0; i < popups.size(); i++) { Widget popup = popups.get(i); if (popup instanceof GPopupPanel && ((GPopupPanel) popup).isModal()) { ((GPopupPanel) popup).addAutoHidePartner(el); } } } public boolean hasPopup() { return popups.size() > 0; } public boolean wasPopupJustClosed() { return justClosedPopup; } public void clearJustClosedPopup() { justClosedPopup = false; } public void unregisterPopup(Widget widget) { popups.remove(widget); } public String getClientType() { if (getLAF() == null) { return "web"; } return getLAF().getType(); } public String getClientID() { return getArticleElement().getDataClientID(); } public boolean isShowToolbar() { if (this.articleElement == null) { return false; } return this.articleElement.getDataParamShowToolBar(false) || this.articleElement.getDataParamApp(); } public int getWidthForSplitPanel(int fallback) { int ret = getAppletWidth() - articleElement.getBorderThickness(); // 2: // border // if it is not 0, there will be some scaling later if (ret <= 0) { ret = fallback; // empirical hack to make room for the toolbar always if (showToolBar() && ret < 598) { ret = 598; // 2: border // maybe this has to be put outside the "if"? } } return ret; } public int getHeightForSplitPanel(int fallback) { int windowHeight = getAppletHeight() - articleElement.getBorderThickness(); // 2: border // but we want to know the available height for the rootPane // so we either use the above as a heuristic, // or we should substract the height(s) of // toolbar, menubar, and input bar; // heuristics come from GeoGebraAppFrame if (showAlgebraInput() && getInputPosition() != InputPosition.algebraView) { windowHeight -= GLookAndFeelI.COMMAND_LINE_HEIGHT; } if (showToolBar() && !isWhiteboardActive()) { windowHeight -= GLookAndFeelI.TOOLBAR_HEIGHT; } // menubar height is always 0 if (windowHeight <= 0) { windowHeight = fallback; } return windowHeight; } protected void initUndoInfoSilent() { getScriptManager().disableListeners(); kernel.initUndoInfo(); getScriptManager().enableListeners(); } @Override public boolean supportsView(int viewID) { if (viewID == App.VIEW_CAS && !getLAF().isSmart()) { if (!Browser.supportsJsCas()) { return false; } } if (viewID == App.VIEW_CAS) { return (getSettings().getCasSettings().isEnabled()) && getArticleElement().getDataParamEnableCAS(true) && getCASFactory().isEnabled(); } if (getLAF() != null && getLAF().examSupported(has(Feature.EXAM_TABLET))) { if (viewID == App.VIEW_EUCLIDIAN) { return getSettings().getEuclidian(1).isEnabled(); } else if (viewID == App.VIEW_EUCLIDIAN2) { return getSettings().getEuclidian(2).isEnabled(); } } return viewID != App.VIEW_EUCLIDIAN3D; } @Override public abstract void set1rstMode(); @Override public int getGUIFontSize() { return 14; } public void updateToolBar() { if (!showToolBar || isIniting()) { return; } if (getGuiManager() != null) { getGuiManager().updateToolbar(); } set1rstMode(); } @Override public void updateApplicationLayout() { Log.debug("updateApplicationLayout: Implementation needed..."); } @Override public void setShowToolBar(boolean toolbar, boolean help) { if (toolbar) { JavaScriptInjector.inject(GuiResourcesSimple.INSTANCE .propertiesKeysJS()); } super.setShowToolBar(toolbar, help); } // methods used just from AppWapplication public int getOWidth() { return 0; } public int getOHeight() { return 0; } public String getAppletId() { return articleElement.getDataParamId(); } public abstract HasAppletProperties getAppletFrame(); /** * @return whether the focus was lost */ private static native Element nativeLoseFocus(Element element) /*-{ var active = $doc.activeElement; if (active && ((active === element) || (active .compareDocumentPosition(element) & $wnd.Node.DOCUMENT_POSITION_CONTAINS))) { active.blur(); return active; } return null; }-*/; @Override public void loseFocus() { // probably this is called on ESC, so the reverse // should happen on ENTER Element ret = nativeLoseFocus(articleElement); if (ret != null) { setLastActive(ret); setAnyAppFocused(false); getGlobalKeyDispatcher().setFocused(false); } } private static void setAnyAppFocused(boolean b) { anyAppHasFocus = b; } /** * @return whether we can focus on ENTER */ private static native boolean nativeGiveFocusBack() /*-{ var active = $doc.activeElement; if (active && (active !== $doc.body)) { //if SVG clicked, getClassName returns non-string if (typeof active.className == "string" && active.className.match(/geogebraweb-dummy-invisible/)) { // actually, ESC focuses this, does not blur! return true; } // this shall execute the default ENTER action on // that element, to be independent (e.g. click on links!) // OR if it is part of GeoGebra, then we shall also not // support selecting GeoGebra again by ENTER //return false; // behold the other return false; // not doing more checks, don't do any action, which is safe return false; } // blurred, probably, so it's safe to focus on ENTER return true; }-*/; /** * Let's say this is the pair of loseFocus, so that only loseFocus can lose * focus from ALL applets officially (i.e. "ESC"), and from each part of * each applet (e.g. input bar, Graphics view, etc), while only * giveFocusBack can give focus back to an applet removed by the loseFocus * method - to avoid hidden bugs. * * What if focus is received by some other method than ENTER (pair of ESC)? * I think let's allow it, but if ENTER comes next, then we should adjust * our knowledge about it (otherwise, it should have been watched in the * entire codebase, which is probably worse, for there are possibilities of * errors). This way just these two methods shall be checked. */ public static void giveFocusBack() { if (anyAppHasFocus) { // here we are sure that ENTER should not do anything return; } // update for the variable in this case, must be made anyway // just it is a question whether this shall also mean a focus? // BUT only when nativeGiveFocusBack is changed, and this // variable also filled perfectly // anyAppHasFocus = true; // here we could insert static aggregates of relevant // variables like getGlobalKeyDispatcher().InFocus // ... but what if e.g. the input bar has focus? // then we can easily check for $doc.activeElement, // whether it means blur=OK if (nativeGiveFocusBack()) { if (lastActiveElement != null) { anyAppHasFocus = true; lastActiveElement.focus(); } } } @Override public boolean isScreenshotGenerator() { return this.articleElement.getDataParamScreenshotGenerator(); } // ======================================================== // INITIALIZING // ======================================================== @Override public void setScrollToShow(boolean b) { if (getGuiManager() != null) { getGuiManager().setScrollToShow(b); } } /** * Overwritten for applets, full app and for touch * * @return {@link MaterialsManagerI} */ public MaterialsManagerI getFileManager() { return null; } // public ToolTipManagerW getToolTipManager(){ // if(toolTipManager == null){ // toolTipManager = new ToolTipManagerW(this); // } // return toolTipManager; // } // ======================================================== // Undo/Redo // ======================================================== @Override public final void storeUndoInfo() { if (isUndoActive()) { kernel.storeUndoInfo(); setUnsaved(); } } // ======================================================== // FILE HANDLING // ======================================================== protected void clearInputBar() { if (isUsingFullGui() && showAlgebraInput() && getGuiManager() != null) { AlgebraInput ai = (getGuiManager().getAlgebraInput()); if (ai != null) { ai.setText(""); } } } // ================================================ // ERROR HANDLING // ================================================ @Override public void showError(String key, String error) { showErrorDialog(getLocalization().getError(key) + ":\n" + error); } public void showMessage(final String message) { getOptionPane().showConfirmDialog(this, message, GeoGebraConstants.APPLICATION_NAME + " - " + getLocalization().getMenu("Info"), GOptionPane.DEFAULT_OPTION, GOptionPane.INFORMATION_MESSAGE, null); } public void showMessage(final String message, final String title) { getOptionPane().showConfirmDialog(this, message, title, GOptionPane.DEFAULT_OPTION, GOptionPane.INFORMATION_MESSAGE, null); } public void showMessage(final String message, final String title, String buttonText, AsyncOperation<String[]> handler) { HTML content = new HTML(message); content.addStyleName("examContent"); ScrollPanel scrollPanel = new ScrollPanel(content); scrollPanel.addStyleName("examScrollPanel"); getOptionPane().showConfirmDialog(this, scrollPanel, title, GOptionPane.DEFAULT_OPTION, GOptionPane.INFORMATION_MESSAGE, buttonText, null, handler); } @Override public void showErrorDialog(final String msg) { if (!isErrorDialogsActive()) { return; } if (this.getErrorHandler() != null) { this.getErrorHandler().showError(msg); return; } } @Override public ErrorHandler getDefaultErrorHandler() { return new ErrorHandler() { @Override public void showError(String msg) { Log.printStacktrace(""); if (!isErrorDialogsActive()) { return; } String title = GeoGebraConstants.APPLICATION_NAME + " - " + getLocalization().getError("Error"); getOptionPane().showConfirmDialog(AppW.this, msg, title, GOptionPane.DEFAULT_OPTION, GOptionPane.ERROR_MESSAGE, null); } @Override public void resetError() { // do nothing } @Override public boolean onUndefinedVariables(String string, AsyncOperation<String[]> callback) { return getGuiManager().checkAutoCreateSliders(string, callback); } @Override public void showCommandError(final String command, String message) { String title = GeoGebraConstants.APPLICATION_NAME + " - " + getLocalization().getError("Error"); String[] optionNames = { getLocalization().getPlain("OK"), getLocalization().getPlain("ShowOnlineHelp") }; getOptionPane().showOptionDialog(AppW.this, message, title, 0, GOptionPane.ERROR_MESSAGE, null, optionNames, new AsyncOperation<String[]>() { @Override public void callback(String[] dialogResult) { if ("1".equals(dialogResult[0])) { if (getGuiManager() != null) { getGuiManager() .openCommandHelp(command); } } } }); } @Override public String getCurrentCommand() { return null; } }; } @Override public void showError(String s) { showErrorDialog(s); } @Override public boolean freeMemoryIsCritical() { // can't access available memory etc from JavaScript return false; } @Override public long freeMemory() { return 0; } @Override public void evalJavaScript(App app, String script0, String arg) { // TODO: maybe use sandbox? String script = script0; String ggbApplet = getDataParamId(); script = "document.ggbApplet= document." + ggbApplet + "; ggbApplet = document." + ggbApplet + ";" + script; // script = "ggbApplet = document.ggbApplet;"+script; // add eg arg="A"; to start if (arg != null) { script = "arg=\"" + arg + "\";" + script; } JsEval.evalScriptNative(script); } public static int getAbsoluteLeft(Element element) { return element.getAbsoluteLeft(); } public static int getAbsoluteRight(Element element) { return element.getAbsoluteRight(); } public static int getAbsoluteTop(Element element) { return element.getAbsoluteTop(); } public static int getAbsoluteBottom(Element element) { return element.getAbsoluteBottom(); } public void attachNativeLoadHandler(ImageElement img) { addNativeLoadHandler(img, getActiveEuclidianView()); } private native void addNativeLoadHandler(ImageElement img, EuclidianView view) /*-{ img .addEventListener( "load", function() { view.@org.geogebra.web.html5.euclidian.EuclidianViewW::updateBackground()(); }); }-*/; // ============================================ // LAYOUT & GUI UPDATES // ============================================ @Override public final boolean showAlgebraInput() { return showAlgebraInput; } @Override public double getWidth() { if (getFrameElement() == null) { return 0; } return getFrameElement().getOffsetWidth(); } @Override public double getHeight() { if (getFrameElement() == null) { return 0; } return getFrameElement().getOffsetHeight(); } private static final int LOWER_HEIGHT = 350; public boolean needsSmallKeyboard() { return (getHeight() > 0 && getHeight() < LOWER_HEIGHT) || (getHeight() == 0 && getArticleElement() .getDataParamHeight() < LOWER_HEIGHT); } @Override public void updateMenubar() { // getGuiManager().updateMenubar(); Log.debug( "AppW.updateMenubar() - implementation needed - just finishing"); // TODO // Auto-generated } @Override public void updateStyleBars() { if (!isUsingFullGui() || isIniting()) { return; } if (getEuclidianView1().hasStyleBar()) { getEuclidianView1().getStyleBar().updateStyleBar(); } if (hasEuclidianView2(1) && getEuclidianView2(1).hasStyleBar()) { getEuclidianView2(1).getStyleBar().updateStyleBar(); } if (has(Feature.DYNAMIC_STYLEBAR)) { updateDynamicStyleBars(); } } @Override public void updateDynamicStyleBars() { if (getEuclidianView1().hasDynamicStyleBar()) { getEuclidianView1().getDynamicStyleBar().updateStyleBar(); } if (hasEuclidianView2(1) && getEuclidianView2(1).hasDynamicStyleBar()) { getEuclidianView2(1).getDynamicStyleBar().updateStyleBar(); } } public void updateCenterPanel() { // only needed with GUI } /** * @param ggwGraphicsViewWidth * * Resets the width of the Canvas converning the Width of its * wrapper (splitlayoutpanel center) */ public void ggwGraphicsViewDimChanged(int width, int height) { // Log.debug("dim changed" + getSettings().getEuclidian(1)); getSettings().getEuclidian(1).setPreferredSize( AwtFactory.getPrototype().newDimension( width, height)); // simple setting temp. appCanvasHeight = height; appCanvasWidth = width; // Log.debug("syn size"); getEuclidianView1().synCanvasSize(); getEuclidianView1().doRepaint2(); stopCollectingRepaints(); } /** * Resets the width of the Canvas converning the Width of its wrapper * (splitlayoutpanel center) * * @param width * , height */ public void ggwGraphicsView2DimChanged(int width, int height) { getSettings().getEuclidian(2).setPreferredSize( AwtFactory.getPrototype().newDimension( width, height)); // simple setting temp. // appCanvasHeight = height; // appCanvasWidth = width; getEuclidianView2(1).synCanvasSize(); getEuclidianView2(1).doRepaint2(); stopCollectingRepaints(); } /** * Resets the width of the Canvas converning the Width of its wrapper * (splitlayoutpanel center) * * @param width * in pixels * @param height * in pixels */ public void ggwGraphicsView3DDimChanged(int width, int height) { // only used for 3D } @Override public void ensureTimerRunning() { this.getTimerSystem().ensureRunning(); } public void showLanguageGUI() { showBrowser(getLanguageGUI()); } @Override public void showCustomizeToolbarGUI() { showBrowser(getCustomizeToolbarGUI()); } protected HeaderPanel getCustomizeToolbarGUI() { // TODO Auto-generated method stub return null; } /** * Overwritten for AppWapplet/AppWapplication * * @param bg */ public void showBrowser(HeaderPanel bg) { // TODO } /** * Overwritten for AppWapplet/AppWapplication */ public HeaderPanel getLanguageGUI() { // TODO Auto-generated method stub return null; } /** * shows the on-screen keyboard (or e.g. a show-keyboard-button) * * @param textField * keyboard listener * @param forceShow * whether it must appear now */ public void showKeyboard(MathKeyboardListener textField, boolean forceShow) { // Overwritten in subclass - nothing to do here } /** * update the on-screen keyboard * * @param field * after the update the input of the keyboard is written into * this field */ public void updateKeyBoardField(MathKeyboardListener field) { // Overwritten in subclass - nothing to do here } /** * hide the on-screen keyboard (if it is visible) */ public void hideKeyboard() { // Overwritten in subclass - nothing to do here } public boolean isOffline() { return !getNetworkOperation().isOnline(); } public boolean isToolLoadedFromStorage() { return toolLoadedFromStorage; } public void setToolLoadedFromStorage(boolean toolLoadedFromStorage) { this.toolLoadedFromStorage = toolLoadedFromStorage; } public void setCloseBrowserCallback(Runnable runnable) { this.closeBroserCallback = runnable; } public void onBrowserClose() { if (this.closeBroserCallback != null) { this.closeBroserCallback.run(); this.closeBroserCallback = null; } } public void addInsertImageCallback(Runnable runnable) { this.insertImageCallback = runnable; } public boolean isMenuShowing() { return false; } /** * @param heightDiff * height difference */ public void addToHeight(int heightDiff) { // for applets with keyboard only } WebsocketLogger webSocketLogger = null; private boolean keyboardNeeded; private String externalPath; @Override public SensorLogger getSensorLogger() { if (webSocketLogger == null) { webSocketLogger = new WebsocketLogger(getKernel()); } return webSocketLogger; } public void setKeyboardNeeded(boolean b) { this.keyboardNeeded = b; } public boolean isKeyboardNeeded() { return keyboardNeeded; } /** * @param perspID * perspective id */ public void showStartTooltip(int perspID) { // probably needed in full version only } public double getAbsLeft() { return this.getFrameElement().getAbsoluteLeft(); } public double getAbsTop() { return this.getFrameElement().getAbsoluteTop(); } public boolean enableFileFeatures() { return this.articleElement.getDataParamEnableFileFeatures(); } public void setPrerelease(String prereleaseStr) { this.canary = false; this.prerelease = false; if ("canary".equals(prereleaseStr)) { canary = true; prerelease = true; } else if ("true".equals(prereleaseStr)) { this.prerelease = true; } } public void hideMenu() { // for applets with menubar } public void setExternalPath(String path) { this.externalPath = path; String title = ""; if (getKernel() != null && getKernel().getConstruction() != null && getKernel().getConstruction().getTitle() == null || "".equals(getKernel().getConstruction().getTitle())) { int lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); title = path.substring(lastSlash + 1).replace(".ggb", ""); getKernel().getConstruction().setTitle(title); } getFileManager().setFileProvider(Provider.LOCAL); } /** * @param runnable * callback for after file is saved */ public void checkSaved(Runnable runnable) { // TODO Auto-generated method stub } /** * * @param base64 * CSV content */ public void openCSV(String base64) { // TODO Auto-generated method stub } protected void updateNavigationBars() { if (showConstProtNavigationNeedsUpdate == null) { return; } for (int key : showConstProtNavigationNeedsUpdate.keySet()) { getGuiManager().getLayout().getDockManager().getPanel(key) .updateNavigationBar(); } } @Override public boolean useShaders() { return true; } /** * @param response * text of OFF file **/ public void openOFF(String response) { // only makes sense in 3D } /** * * @return Pixel ratio including external transforms */ public double getPixelRatio() { return Browser.getPixelRatio() * articleElement.readScaleX(); } private ArrayList<MouseTouchGestureControllerW> euclidianHandlers = new ArrayList<MouseTouchGestureControllerW>(); public void addWindowResizeListener(MouseTouchGestureControllerW mtg) { this.euclidianHandlers.add(mtg); } public boolean showToolBarHelp() { return getArticleElement().getDataParamShowToolBarHelp(true); } public Panel getPanel() { return RootPanel.get(); } private Timer altTextTimer = new Timer(){ @Override public void run() { getEuclidianView1().setAltText(); if (hasEuclidianView2(1)) { getEuclidianView2(1).setAltText(); } if (isEuclidianView3Dinited()) { ((EuclidianViewWInterface) getEuclidianView3D()).setAltText(); } } }; @Override public void setAltText() { altTextTimer.schedule(700); } @Override public GImageIcon wrapGetModeIcon(int mode) { return null; } @Override public boolean showAutoCreatedSlidersInEV() { return false; } public boolean allowStylebar() { return (!isApplet() || getArticleElement().getDataParamShowMenuBar(false) || getArticleElement().getDataParamAllowStyleBar(false)) && enableGraphing(); } public boolean enableGraphing() { return getArticleElement().getDataParamEnableGraphing(true); } public boolean isStartedWithFile() { return getArticleElement().getDataParamFileName().length() > 0 || getArticleElement().getDataParamBase64String().length() > 0 || getArticleElement().getDataParamTubeID().length() > 0 || this.getArticleElement().getDataParamJSON().length() > 0; } @Override public GTimer newTimer(GTimerListener listener, int delay) { return new GTimerW(listener, delay); } public void updateMaterialURL(int i, String object) { setTubeId(i); if (articleElement.getDataParamApp() && object !=null) { Browser.changeUrl("o/" + object); } } public void updateRounding(){ setRounding(getArticleElement().getDataParamRounding()); } /* * public Runnable showPerspectivesPopupRunnable() { MainMenu menu = * (MainMenu) laf.getMenuBar(this); return new Runnable() { public void * run() { menu.getPerspectivesMenuW().showPerspectivesPopup(); } }; } */ public void showPerspectivesPopup() { // overridden in AppWFull } public void closePerspectivesPopup() { // only for GUI } /** * @param index * perspective ID */ public void setActivePerspective(int index) { // only for GUI } public void addFocusToApp() { if (!GlobalKeyDispatcherW.getIsHandlingTab()) { getGlobalKeyDispatcher().setFocused(true); return; } // add focus to AV if visible AlgebraView av = getAlgebraView(); boolean visible = av != null && av.isShowing(); if (visible) { ((Widget) av).getElement().focus(); focusGained(av, ((Widget) av).getElement()); return; } // focus -> EV EuclidianViewW ev = getEuclidianView1(); visible = ev != null && ev.isShowing(); if (visible) { ev.getCanvas().getElement().focus(); ev.focusGained(); } } public void resetViewsEnabled() { // reset cas and 3d settings for restart of exam // needed for GGB-1015 this.getSettings().getCasSettings().resetEnabled(); this.getSettings().getEuclidian(-1).resetEnabled(); getSettings().getEuclidian(1).resetEnabled(); getSettings().getEuclidian(2).resetEnabled(); setViewsEnabled(); } public boolean isPerspectivesPopupVisible() { return false; } /** * @param p * perspective to be forced after settings */ public void loadPreferences(Perspective p) { // GeoGebraPreferencesW.getPref().loadForApp(app, p); } @Override public void ensureEvSizeSet(EuclidianSettings evSet) { GDimension gd = evSet.getPreferredSize(); if (gd.getWidth() == 0 || gd.getHeight() == 0) { int width = getAppletWidth() - 2; // 2: border int height = getAppletHeight() - 2; // 2: border if (width == 0 || height == 0) { // setting a standard size, like in // compabilityLayout // fixing a real bug of height 0 width = 598; // 2: border height = 438; // 2: border } evSet.setPreferredSize( AwtFactory.getPrototype().newDimension(width, height)); } } @Override public void setNewExam() { setExam(new ExamEnvironmentW(this)); } /** * * @return 9999 (or 200 in web) */ @Override public int getMaxSpreadsheetRowsVisible() { return Kernel.MAX_SPREADSHEET_ROWS_WEB; } /** * * @return 9999 (or 200 in web) */ @Override public int getMaxSpreadsheetColumnsVisible() { return Kernel.MAX_SPREADSHEET_COLUMNS_WEB; } @Override public GeoGebraToPstricks newGeoGebraToPstricks() { return new GeoGebraToPstricksW(this); } @Override public GeoGebraToAsymptote newGeoGebraToAsymptote() { return new GeoGebraToAsymptoteW(this); } @Override public GeoGebraToPgf newGeoGebraToPgf() { return new GeoGebraToPgfW(this); } public static native Element getHiddenTextArea() /*-{ var hiddenTextArea = $doc.getElementById('hiddenCopyPasteTextArea'); if (!hiddenTextArea) { hiddenTextArea = $doc.createElement("textarea"); hiddenTextArea.id = 'hiddenCopyPasteTextArea'; hiddenTextArea.style.position = 'absolute'; hiddenTextArea.style.width = '10px'; hiddenTextArea.style.height = '10px'; hiddenTextArea.style.zIndex = '100'; hiddenTextArea.style.left = '-1000px'; hiddenTextArea.style.top = '0px'; $doc.getElementsByTagName('body')[0].appendChild(hiddenTextArea); } //hiddenTextArea.value = ''; return hiddenTextArea; }-*/; /** * https://jsfiddle.net/alvaroAV/a2pt16yq/ works in IE11, Chrome, Firefox, * Edge * * this method doesn't always work in Edge, Firefox as needs to be run from * eg button * * @param value * text to copy */ public static native void copyToSystemClipboardNative(String value) /*-{ var copyFrom = @org.geogebra.web.html5.main.AppW::getHiddenTextArea()(); copyFrom.value = value; copyFrom.select(); $doc.execCommand('copy'); }-*/; @Override public void copyTextToSystemClipboard(String text) { Log.debug("copying to clipboard " + text); copyToSystemClipboardNative(text); } public void copyTextToSystemClipboard(String text, Runnable notify) { Log.debug("copying to clipboard " + text); copyToSystemClipboardNative(text); if (notify != null) { notify.run(); } } private static void setLastActive(Element e) { lastActiveElement = e; } public void toggleMenu() { // only with GUI } public int getActivePerspective() { return 0; } @Override public boolean nativeCAS() { return Browser.externalCAS(); } public void onUnhandledClick() { // TODO Auto-generated method stub } public final void updateCenterPanelAndViews() { updateCenterPanel(); updateViewSizes(); } public void updateSplitPanelHeight() { // implemented in subclass } }