package org.geogebra.web.html5.kernel; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.UndoManager; import org.geogebra.common.main.App; import org.geogebra.common.plugin.Event; import org.geogebra.common.plugin.EventType; import org.geogebra.common.util.debug.Log; import org.geogebra.web.html5.Browser; import com.google.gwt.storage.client.Storage; /** * Undo manager using session storage */ public class UndoManagerW extends UndoManager { private static final String TEMP_STORAGE_PREFIX = "GeoGebraUndoInfo"; /** state counter */ static long nextKeyNum = 1; /** * can be null (eg IE9 running locally) */ Storage storage; /** * Storage state */ protected class AppStateWeb implements AppState { private String key; private String xml; /** * @param xmls * XML */ AppStateWeb(String xmls) { if (storage != null) { storage.setItem(key = TEMP_STORAGE_PREFIX + nextKeyNum++, xmls); } else { xml = xmls; } } /** * @return XML */ public String getXML() { if (storage == null) { return xml; } return storage.getItem(key); } @Override public void delete() { xml = null; if (storage != null) { storage.removeItem(key); } } } /** * @param cons * construction */ public UndoManagerW(Construction cons) { super(cons); if (Browser.supportsSessionStorage()) { storage = Storage.getSessionStorageIfSupported(); } else { Log.warn("Session storage not supported"); } } @Override public void processXML(String xml) throws Exception { construction.getXMLio().processXMLString(xml, true, false, true, false); } @Override public void storeUndoInfoAfterPasteOrAdd() { // this can cause a java.lang.OutOfMemoryError for very large // constructions final StringBuilder currentUndoXML = construction .getCurrentUndoXML(true); // Thread undoSaverThread = new Thread() { // @Override // public void run() { doStoreUndoInfo(currentUndoXML); app.getCopyPaste().pastePutDownCallback(app); // } // }; // undoSaverThread.start(); } @Override public void storeUndoInfo(final StringBuilder currentUndoXML, final boolean refresh) { // Thread undoSaverThread = new Thread() { // @Override // public void run() { doStoreUndoInfo(currentUndoXML); if (refresh) { restoreCurrentUndoInfo(); } // } // }; // undoSaverThread.start(); } /** * Adds construction state to undo info list. * * @param undoXML * string builder with construction XML */ synchronized void doStoreUndoInfo(final StringBuilder undoXML) { try { // insert undo info AppState appStateToAdd = new AppStateWeb(undoXML.toString()); iterator.add(appStateToAdd); pruneStateList(); app.getEventDispatcher().dispatchEvent( new Event(EventType.STOREUNDO, null)); } catch (Exception e) { Log.debug("storeUndoInfo: " + e.toString()); e.printStackTrace(); } catch (Error err) { Log.debug("UndoManager.storeUndoInfo: " + err.toString()); err.printStackTrace(); } updateUndoActions(); } @Override protected void loadUndoInfo(final AppState info) { try { // load from file String tempXML = ((AppStateWeb) info).getXML(); if (tempXML == null) { Log.error("Undo not supported."); } // make sure objects are displayed in the correct View app.setActiveView(App.VIEW_EUCLIDIAN); // load undo info app.getScriptManager().disableListeners(); processXML(tempXML); app.getScriptManager().enableListeners(); // If there are Exercises we also have to update the Exercises if (app.getKernel().hasExercise()) { app.getKernel().getExercise().notifyUpdate(); } } catch (Exception e) { e.printStackTrace(); restoreCurrentUndoInfo(); Log.error("Undo exception:" + e.getMessage()); } catch (Error err) { Log.error("Undo error:" + err.getMessage()); } } }