/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ package org.geogebra.desktop.main; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Locale; import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JPanel; import org.geogebra.common.GeoGebraConstants; import org.geogebra.common.jre.util.Base64; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.util.FileExtensions; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.debug.Log; import org.geogebra.desktop.AppletImplementationInterface; import org.geogebra.desktop.CommandLineArguments; import org.geogebra.desktop.euclidian.EuclidianViewD; import org.geogebra.desktop.gui.GuiManagerD; import org.geogebra.desktop.plugin.GgbAPID; import geogebra.GeoGebraAppletPreloader; /** * GeoGebra applet implementation operating on a given JApplet object. */ public class AppletImplementation implements AppletImplementationInterface { public JApplet applet; protected AppD app; protected Kernel kernel; private EuclidianViewD ev; public boolean undoActive; public boolean showToolBar, showToolBarHelp, showAlgebraInput, allowStyleBar; public boolean enableRightClick = true; public boolean useBrowserForJavaScript = true; public boolean errorDialogsActive = true; public boolean enableLabelDrags = true; boolean enableShiftDragZoom = true; boolean allowRescaling = true; public boolean showMenuBar = false; // public boolean showSpreadsheet = false; // public boolean showAlgebraView = false; boolean showResetIcon = false; Color bgColor, borderColor; private String fileStr, customToolBar; private int maxIconSize; public int width, height; // public static URL codeBase=null; // public static URL documentBase=null; // private JavaScriptMethodHandler javaScriptMethodHandler; // private boolean javascriptLoadFile=false, javascriptReset=false; // private String javascriptLoadFileName=""; private GgbAPID ggbApi = null; // Ulven 29.05.08 public String ggbOnInitParam = null; private String perspective; /** Creates a new instance of GeoGebraApplet */ public AppletImplementation(final JApplet applet) { this.applet = applet; // Allow rescaling eg ctrl+ ctrl- in Firefox applet.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { Component c = e.getComponent(); Log.debug("Applet resized to: " + c.getWidth() + ", " + c.getHeight()); if (allowRescaling && (app != null) && !app.runningInFrame && app.onlyGraphicsViewShowing()) { // use just horizontal scale factors // under normal circumstances, these should be the same double zoomFactor = (double) c.getWidth() / (double) width;// (double)c.getHeight() // / // (double)height // ; app.getEuclidianView1().zoomAroundCenter(zoomFactor); } // these always need updating eg draw reset icon, play/pause // icon width = c.getWidth(); height = c.getHeight(); } }); init(); } @Override public void dispose() { app = null; kernel = null; ev = null; } /** * Initializes the CAS, GUI components, and downloads jar files in a * separate thread. */ @Override public void initInBackground() { Log.debug("initInBackground"); // start animation if wanted by ggb file if (kernel.wantAnimationStarted()) { kernel.getAnimatonManager().startAnimation(); } // call JavaScript function ggbOnInit() initJavaScript(); Object[] noArgs = {}; Object[] arg = { ggbOnInitParam }; Log.debug("calling ggbOnInit(" + (((ggbOnInitParam == null) ? "" : ggbOnInitParam)) + ")"); app.getScriptManager().callJavaScript("ggbOnInit", (ggbOnInitParam == null) ? noArgs : arg); // give applet time to repaint Thread initingThread = new Thread() { @Override public void run() { // wait a bit for applet to draw first time // then start background initing of GUI elements try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // load all jar files in background GeoGebraAppletPreloader .loadAllJarFiles(!app.useBrowserForJavaScript()); } }; initingThread.start(); } private void init() { // codeBase=this.getCodeBase(); // documentBase=this.getDocumentBase(); // Application.debug("codeBase="+codeBase); // Application.debug("documentBase="+documentBase); // get parameters // filename of construction fileStr = applet.getParameter("filename"); if ((fileStr != null) && !(fileStr.startsWith("http") || fileStr.startsWith("file"))) { // add document base to file name URL base = applet.getDocumentBase(); String documentBase = base.toString(); if (fileStr.charAt(0) == '/') { fileStr = base.getProtocol() + "://" + base.getHost() + fileStr; } else { String path = documentBase.substring(0, documentBase.lastIndexOf('/') + 1); fileStr = path + fileStr; } } else { // check if ggb file is encoded as base 64 String fileBase64 = applet.getParameter("ggbBase64"); if (fileBase64 != null) { fileStr = "base64://" + fileBase64; } } Log.debug("loading " + fileStr); // showToolBar = "true" or parameter is not available showToolBar = "true".equals(applet.getParameter("showToolBar")); // showToolBar = "true" or parameter is not available showToolBarHelp = showToolBar && "true".equals(applet.getParameter("showToolBarHelp")); // customToolBar = "0 1 2 | 3 4 5 || 7 8 12" to set the visible toolbar // modes customToolBar = applet.getParameter("customToolBar"); perspective = applet.getParameter("perspective"); if (perspective == null) { perspective = ""; } // showMenuBar = "true" or parameter is not available showMenuBar = "true".equals(applet.getParameter("showMenuBar")); // showSpreadsheet = "true" or parameter is not available // showSpreadsheet = // "true".equals(applet.getParameter("showSpreadsheet")); // showAlgebraView = "true" or parameter is not available // showAlgebraView = // "true".equals(applet.getParameter("showAlgebraView")); // showResetIcon = "true" or parameter is not available showResetIcon = "true".equals(applet.getParameter("showResetIcon")); // showAlgebraInput = "true" or parameter is not available showAlgebraInput = "true" .equals(applet.getParameter("showAlgebraInput")); // default is true useBrowserForJavaScript = !"false" .equals(applet.getParameter("useBrowserForJS")); // show style bar of views, default is false allowStyleBar = "true".equals(applet.getParameter("allowStyleBar")); // rightClickActive, default is "true" enableRightClick = !"false" .equals(applet.getParameter("enableRightClick")); // errorDialogsActive, default is "true" errorDialogsActive = !"false" .equals(applet.getParameter("errorDialogsActive")); // enableLabelDrags, default is "true" enableLabelDrags = !"false" .equals(applet.getParameter("enableLabelDrags")); // paramter for JavaScript ggbOnInit() call ggbOnInitParam = applet.getParameter("ggbOnInitParam"); // enableShiftDragZoom, default is "true" enableShiftDragZoom = !"false" .equals(applet.getParameter("enableShiftDragZoom")); // allowRescaling, default is "false" allowRescaling = "true".equals(applet.getParameter("allowRescaling")); undoActive = (showToolBar || showMenuBar); // set language manually by iso language string String language = applet.getParameter("language"); String country = applet.getParameter("country"); Locale loc = null; if (language != null) { if (country != null) { loc = new Locale(language, country); } else { loc = new Locale(language); } applet.setLocale(loc); } // bgcolor = "#CCFFFF" specifies the background color to be used for // the button panel try { bgColor = Color.decode(applet.getParameter("bgcolor")); } catch (Exception e) { bgColor = Color.white; } // borderColor = "#CCFFFF" specifies the border color to be used for // the applet panel try { borderColor = Color.decode(applet.getParameter("borderColor")); } catch (Exception e) { borderColor = Color.gray; } // maximum icon size to be used in the toolbar try { maxIconSize = Integer.parseInt(applet.getParameter("maxIconSize")); } catch (Exception e) { maxIconSize = AppD.DEFAULT_ICON_SIZE; } // build application and open file /* * if (fileStr == null) { app = new CustomApplication(null, this, * undoActive); } else { String[] args = { fileStr }; app = new * CustomApplication(args, this, undoActive); } */ AppD.setLAF(AppD.MAC_OS || AppD.WINDOWS); if (fileStr == null) { app = buildApplication(null, undoActive); } else { String[] args = { fileStr }; app = buildApplication(new CommandLineArguments(args), undoActive); } // needed to make sure unicodeZero works if (loc != null) { app.setLanguage(loc); } kernel = app.getKernel(); this.ggbApi = app.getGgbApi(); } protected AppD buildApplication(CommandLineArguments args, boolean undoActive) { return new AppD(args, this, undoActive); } /** * @return If the applet parameters indicate that the GUI is necessary. */ public boolean needsGui() { return showAlgebraInput || showToolBar || showMenuBar || enableRightClick; } public void initGUI() { JPanel myContenPane; // show interactive drawing pad // TODO use Appication methods (F.S.) // create applet panel myContenPane = createGeoGebraAppletPanel(); // border around applet panel myContenPane.setBorder(BorderFactory.createLineBorder(borderColor)); // replace applet's content pane Container cp = applet.getContentPane(); Log.debug("Initial size = " + cp.getWidth() + ", " + cp.getHeight()); // Application.debug("EuclidianView size = // "+app.getEuclidianView().getPreferredSize().getWidth()+", // "+app.getEuclidianView().getPreferredSize().getHeight()); width = cp.getWidth(); height = cp.getHeight(); if (originalWidth < 0) { setInitialScaling(); } cp.setBackground(bgColor); cp.removeAll(); cp.add(myContenPane); // set move mode app.setMoveMode(); } private int originalWidth = -1, originalHeight = -1; /* * rescales if the width is not what's expected eg if browser is zoomed */ private void setInitialScaling() { if (allowRescaling) { if (!app.runningInFrame && app.onlyGraphicsViewShowing()) { originalWidth = (int) ev.getPreferredSize().getWidth(); originalHeight = (int) ev.getPreferredSize().getHeight(); double zoomFactorX = (double) width / (double) originalWidth; double zoomFactorY = (double) height / (double) originalHeight; double zoomFactor = Math.min(zoomFactorX, zoomFactorY); ev.zoomAroundCenter(zoomFactor); } else { originalWidth = width; originalHeight = height; } } } protected JPanel createGeoGebraAppletPanel() { JPanel appletPanel = new JPanel(new BorderLayout()); appletPanel.setBackground(bgColor); app.setUndoActive(undoActive); app.setShowMenuBar(showMenuBar); // app.setShowSpreadsheetView(showSpreadsheet); // app.setShowAlgebraView(showAlgebraView); app.setShowAlgebraInput(showAlgebraInput, true); app.setUseBrowserForJavaScript(useBrowserForJavaScript); app.setShowToolBar(showToolBar, showToolBarHelp); app.setRightClickEnabled(enableRightClick); app.setPerspectiveParam(perspective); app.setErrorDialogsActive(errorDialogsActive); app.setLabelDragsEnabled(enableLabelDrags); app.setShiftDragZoomEnabled(enableShiftDragZoom); if ((customToolBar != null) && (customToolBar.length() > 0) && showToolBar) { app.getGuiManager().setToolBarDefinition(customToolBar); } app.setShowResetIcon(showResetIcon); app.getImageManager().setMaxIconSize(maxIconSize); app.getSettings().getLayout().setAllowStyleBar(allowStyleBar); appletPanel.add(app.buildApplicationPanel(), BorderLayout.CENTER); ev = app.getEuclidianView1(); ev.updateBackground(); return appletPanel; } public void showApplet() { Thread worker = new Thread() { @Override public void run() { applet.setCursor( Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); reinitGUI(); applet.setCursor(Cursor.getDefaultCursor()); } }; worker.start(); } private void reinitGUI() { app.runningInFrame = false; Container cp = applet.getContentPane(); cp.removeAll(); app.setApplet(this); if (app.isUsingFullGui()) { ((GuiManagerD) app.getGuiManager()).updateLayout(); } initGUI(); app.resetFonts(); app.refreshViews(); applet.validate(); } /* JAVA SCRIPT INTERFACE */ /* * Rewritten by Ulven 29.05.08: Moved method contents to GgbAPI and put in * redirections to GgbApi. (Oneliners left as they are, nothing to gain...) */ /** * Opens construction given in XML format. May be used for loading * constructions. */ @Override public synchronized void setXML(String xml) { app.setXML(xml, true); reinitGUI(); } /** * Opens construction given in Base64 format. May be used for loading * constructions. */ public synchronized void setBase64(final String base64) { // base64 might contain an image, calls ImageIO etc AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { // perform the security-sensitive operation here getGgbApi().setBase64(base64); return null; } }); } /** * Evaluates the given XML string and changes the current construction. * Note: the construction is NOT cleared before evaluating the XML string. */ @Override public synchronized void evalXML(String xmlString) { StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sb.append("<geogebra format=\"" + GeoGebraConstants.XML_FILE_FORMAT + "\">\n"); ev.getXML(sb, false); sb.append("<construction>\n"); sb.append(xmlString); sb.append("</construction>\n"); sb.append("</geogebra>\n"); app.setXML(sb.toString(), false); } /** * Evaluates the given string as if it was entered into GeoGebra CAS's input * text field. * * @return evaluation result in GeoGebraCAS syntax */ @Override public synchronized String evalGeoGebraCAS(String cmdString) { return evalGeoGebraCAS(cmdString, false); } /** * Evaluates the given string as if it was entered into the GeoGebraCAS * input text field. * * @param debugOutput * states whether debugging information should be printed to the * console * @return evaluation result in GeoGebraCAS syntax */ private synchronized String evalGeoGebraCAS(final String cmdString, final boolean debugOutput) { // avoid security problems calling from JavaScript return AccessController.doPrivileged(new PrivilegedAction<String>() { @Override public String run() { return getGgbApi().evalGeoGebraCAS(cmdString, debugOutput); } }); } /** * Resets the initial construction (given in filename parameter) of this * applet. */ @Override public synchronized void reset() { if (fileStr == null) { return; } if (fileStr.startsWith("base64://")) { byte[] zipFile; try { zipFile = Base64.decode(fileStr.substring(9)); } catch (Exception e) { e.printStackTrace(); return; } app.loadXML(zipFile); } else { // avoid security problems calling from JavaScript AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { // perform the security-sensitive operation here app.setWaitCursor(); try { URL ggbURL = new URL(fileStr); app.loadXML(ggbURL, StringUtil.getFileExtension(fileStr) .equals(FileExtensions.GEOGEBRA_TOOL)); reinitGUI(); applet.validate(); } catch (Exception e) { e.printStackTrace(); } app.setDefaultCursor(); return null; } }); } if (allowRescaling) { ev.setTemporarySize(originalHeight, originalWidth); double zoomFactorX = (double) width / (double) originalWidth; double zoomFactorY = (double) height / (double) originalHeight; double zoomFactor = Math.min(zoomFactorX, zoomFactorY); ev.zoomAroundCenter(zoomFactor); ev.setTemporarySize(-1, -1); } } /** * Loads a construction from a file (given URL). */ @Override public synchronized void openFile(final String strURL) { // avoid security problems calling from JavaScript AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { // perform the security-sensitive operation here // load file app.setWaitCursor(); try { String myStrURL = strURL; String lowerCase = StringUtil.toLowerCase(strURL); if (!(lowerCase.startsWith("http") || lowerCase.startsWith("file"))) { myStrURL = applet.getCodeBase() + myStrURL; } URL ggbURL = new URL(myStrURL); app.loadXML(ggbURL, StringUtil.getFileExtension(lowerCase) .equals(FileExtensions.GEOGEBRA_TOOL)); reinitGUI(); } catch (Exception e) { e.printStackTrace(); } app.setDefaultCursor(); return null; } }); } /** * @return The border color of the applet. */ public Color getBorderColor() { return borderColor; } /* * Methods to change the geometry window's properties */ /** * returns a String (base-64 encoded PNG file of the Graphics View) */ @Override public synchronized String getPNGBase64(final double exportScale, final boolean transparent, final double DPI, final boolean copyToClipboard) { // avoid security problems calling from JavaScript return AccessController.doPrivileged(new PrivilegedAction<String>() { @Override public String run() { // perform the security-sensitive operation here return getGgbApi().getPNGBase64(exportScale, transparent, DPI, copyToClipboard); } }); } /** * returns a String (base-64 encoded PNG file of the Graphics View) */ @Override public synchronized boolean writePNGtoFile(final String filename, final double exportScale, final boolean transparent, final double DPI) { // avoid security problems calling from JavaScript Boolean b = AccessController .doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { // perform the security-sensitive operation here return getGgbApi().writePNGtoFile(filename, exportScale, transparent, DPI); } }); return b.booleanValue(); } public synchronized void initJavaScript() { /* * if (!app.useBrowserForJavaScript()) { return; } * * if (browserWindow == null) { try { browserWindow = * JSObject.getWindow(applet); * * if (browserWindow == null) { Log.debug( * "Warning: could not initialize JSObject.getWindow() for GeoGebraApplet" * ); } * * } catch (Exception e) { Log.debug( * "Exception: could not initialize JSObject.getWindow() for GeoGebraApplet" * ); } } */ } public Object callJavaScript(String jsFunction, Object[] args) { // Application.debug("callJavaScript: " + jsFunction); // initJavaScript(); // // try { // if (browserWindow != null) { // Log.debug("callJavaScript: " + jsFunction); // return browserWindow.call(jsFunction, args); // } // // Log.debug("Warning: could not initialize JSObject.getWindow() for // GeoGebraApplet when calling " // + jsFunction); // } catch (Exception e) { // Log.error("Warning: Error calling JavaScript function '" // + jsFunction + "' (" + e.getLocalizedMessage() + ")"); // // e.printStackTrace(); // } return null; } public JApplet getJApplet() { return applet; } @Override public GgbAPID getGgbApi() { return ggbApi; } }