package org.geogebra.web.web.gui.view.algebra;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import org.geogebra.common.gui.view.algebra.AlgebraController;
import org.geogebra.common.gui.view.algebra.AlgebraView;
import org.geogebra.common.io.layout.PerspectiveDecoder;
import org.geogebra.common.javax.swing.SwingConstants;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.LayerView;
import org.geogebra.common.kernel.ModeSetter;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoCurveCartesian;
import org.geogebra.common.kernel.algos.AlgoDependentText;
import org.geogebra.common.kernel.geos.GProperty;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.main.App;
import org.geogebra.common.main.App.InputPosition;
import org.geogebra.common.main.Feature;
import org.geogebra.common.main.GeoElementSelectionListener;
import org.geogebra.common.main.Localization;
import org.geogebra.common.main.settings.AbstractSettings;
import org.geogebra.common.main.settings.AlgebraSettings;
import org.geogebra.common.main.settings.SettingListener;
import org.geogebra.common.plugin.EventType;
import org.geogebra.common.util.debug.GeoGebraProfiler;
import org.geogebra.common.util.debug.Log;
import org.geogebra.web.html5.awt.PrintableW;
import org.geogebra.web.html5.gui.util.CancelEventTimer;
import org.geogebra.web.html5.main.AppW;
import org.geogebra.web.html5.main.DrawEquationW;
import org.geogebra.web.html5.main.TimerSystemW;
import org.geogebra.web.web.css.GuiResources;
import org.geogebra.web.web.gui.GuiManagerW;
import org.geogebra.web.web.gui.layout.DockSplitPaneW;
import org.geogebra.web.web.gui.layout.panels.AlgebraDockPanelW;
import org.geogebra.web.web.gui.layout.panels.AlgebraStyleBarW;
import org.geogebra.web.web.util.ReTeXHelper;
import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.DragStartEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasTreeItems;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.ProvidesResize;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
/**
* HTML5 version of AV
*
*/
public class AlgebraViewW extends Tree implements LayerView, AlgebraView,
OpenHandler<TreeItem>, SettingListener, ProvidesResize, PrintableW {
/**
* Flag for LaTeX rendering
*/
/** app */
protected final AppW app; // parent appame
/** Localization */
protected final Localization loc;
/** Kernel */
protected final Kernel kernel;
private AnimationScheduler repaintScheduler = AnimationScheduler.get();
// protected AlgebraInputW inputPanel;
/** Input item */
RadioTreeItem inputPanelLatex;
private AlgebraStyleBarW styleBar;
private boolean editItem = false;
private GeoElement draggedGeo;
// to store width if original was thiner than needed.
private Integer originalWidth = null;
private AnimationScheduler.AnimationCallback repaintCallback = new AnimationScheduler.AnimationCallback() {
@Override
public void execute(double ts) {
doRepaint();
}
};
private AnimationScheduler.AnimationCallback repaintSlidersCallback = new AnimationScheduler.AnimationCallback() {
@Override
public void execute(double ts) {
doRepaintSliders();
}
};
/**
* The mode of the tree, see MODE_DEPENDENCY, MODE_TYPE
*/
protected SortMode treeMode = SortMode.ORDER;
private boolean showAuxiliaryObjectsSettings = false;
private boolean settingsChanged = false;
/**
* Nodes for tree mode MODE_DEPENDENCY
*/
private TreeItem depNode, indNode;
private TreeItem auxiliaryNode;
/**
* Root node for tree mode MODE_TYPE.
*/
private TreeItem rootType;
/**
* Nodes for tree mode MODE_TYPE
*/
private HashMap<String, TreeItem> typeNodesMap;
/* for SortMode.ORDER */
private TreeItem rootOrder;
/* for SortMode.LAYER */
private TreeItem rootLayer;
private HashMap<Integer, TreeItem> layerNodesMap;
private HashMap<GeoElement, TreeItem> nodeTable = new HashMap<GeoElement, TreeItem>(
500);
private int waitForRepaint = TimerSystemW.SLEEPING_FLAG;
private StringBuilder sbXML;
private RadioTreeItem activeItem;
// private AlgebraHelperBar helperBar;
private AlgebraController algebraController;
private AVSelectionController selectionCtrl;
/**
* @return algebra controller
*/
public AlgebraController getAlgebraController() {
return algebraController;
}
/**
* Creates new AV
*
* @param algCtrl
* controller
*/
public AlgebraViewW(AlgebraController algCtrl) {
super(new TreeImages());
Log.debug("creating Algebra View");
this.algebraController = algCtrl;
this.app = (AppW) algCtrl.getApplication();
this.loc = app.getLocalization();
this.kernel = app.getKernel();
this.addOpenHandler(this);
selectionCtrl = new AVSelectionController(app);
algCtrl.setView(this);
if (algCtrl instanceof AlgebraControllerW) {
initGUI((AlgebraControllerW) algCtrl);
}
app.getSelectionManager()
.addSelectionListener(new GeoElementSelectionListener() {
@Override
public void geoElementSelected(GeoElement geo,
boolean addToSelection) {
updateSelection();
}
});
}
/**
* Scroll handler
*/
void onAlgebraScroll() {
if (activeItem != null) {
activeItem.reposition();
}
if (getInputTreeItem() != null) {
getInputTreeItem().setItemWidth(maxItemWidth);
}
}
private void initGUI(AlgebraControllerW algCtrl) {
// add listener
this.addDomHandler(algCtrl, MouseDownEvent.getType());
this.addDomHandler(algCtrl, MouseMoveEvent.getType());
this.addDomHandler(algCtrl,
TouchStartEvent.getType());
this.addDomHandler(algCtrl, TouchEndEvent.getType());
this.addDomHandler(algCtrl, TouchMoveEvent.getType());
// initializes the tree model, important to set tree mode first to avoid
// inf. loop #3651
treeMode = app.getSettings().getAlgebra().getTreeMode();
initModel();
setLabels();
getElement().addClassName("algebraView");
// needed to have an element with tabindex > 0 with focus to catch
// keyboard events
this.getElement().setAttribute("tabindex", "5000");
this.addKeyDownHandler(this.app.getGlobalKeyDispatcher());
this.addKeyUpHandler(this.app.getGlobalKeyDispatcher());
this.addKeyPressHandler(this.app.getGlobalKeyDispatcher());
this.setFocus(true);
// Initialize settings and register listener
app.getSettings().getAlgebra().addListener(this);
settingsChanged(app.getSettings().getAlgebra());
}
@Override
public void onBrowserEvent(Event event) {
// as arrow keys are prevented in super.onBrowserEvent,
// we need to handle arrow key events before that
switch (DOM.eventGetType(event)) {
default:
// do nothing
break;
case Event.ONKEYUP:
switch (event.getKeyCode()) {
default:
// do nothing
break;
case KeyCodes.KEY_UP:
case KeyCodes.KEY_DOWN:
case KeyCodes.KEY_LEFT:
case KeyCodes.KEY_RIGHT:
// this may be enough for Safari too, because it is not
// onkeypress
if (!editItem) {
app.getGlobalKeyDispatcher().handleSelectedGeosKeysNative(
event);
event.stopPropagation();
event.preventDefault();
return;
}
//TODO: check this ----
break;
case KeyCodes.KEY_TAB:
if (!app.getGlobalKeyDispatcher().isFocused()) {
return;
}
// ----
}
break;
case Event.ONMOUSEDOWN:
case Event.ONTOUCHSTART:
app.closePopups();
// see this.setFocus(true) and this.addKeyDownHandler...
app.focusGained(AlgebraViewW.this, this.getElement());
}
if (!editItem) {
if (event.getTypeInt() == Event.ONCLICK) {
// background click
if (!CancelEventTimer.cancelKeyboardHide()
&& !CancelEventTimer.cancelMouseEvent()) {
// maybe another focusScheduled is called, but
// that should not be a problem, the problem should
// collect blur events all along the way
app.getGuiManager().focusScheduled(true, true, true);
app.hideKeyboard();
}
}
super.onBrowserEvent(event);
}
}
/**
* schedule a repaint
*/
public void deferredRepaint() {
repaintScheduler.requestAnimationFrame(repaintCallback);
}
/**
* Repaint sliders in next animation frame
*/
public void deferredRepaintSliders() {
repaintScheduler.requestAnimationFrame(repaintSlidersCallback);
}
/**
* timer system suggests a repaint
*/
@Override
public boolean suggestRepaint(){
// repaint sliders as fast as possible
if (isShowing()) {
deferredRepaintSliders();
}
if (waitForRepaint == TimerSystemW.SLEEPING_FLAG){
return false;
}
if (waitForRepaint == TimerSystemW.REPAINT_FLAG){
if (isShowing()){
deferredRepaint();
waitForRepaint = TimerSystemW.SLEEPING_FLAG;
}
return true;
}
waitForRepaint--;
return true;
}
@Override
public final void repaintView() {
app.ensureTimerRunning();
if (waitForRepaint == TimerSystemW.SLEEPING_FLAG){
waitForRepaint = TimerSystemW.ALGEBRA_LOOPS;
}
}
/**
* Make sure we repaint all updated objects in nodes that were collapsed before
*/
@Override
public void onOpen(OpenEvent<TreeItem> event){
this.deferredRepaint();
}
@Override
public final int getViewID() {
return App.VIEW_ALGEBRA;
}
// TODO EuclidianView#setHighlighted() doesn't exist
/**
* updates node of GeoElement geo (needed for highlighting)
*
*/
@Override
public void update(GeoElement geo) {
long start = System.currentTimeMillis();
TreeItem node = nodeTable.get(geo);
if (node != null) {
RadioTreeItem item = RadioTreeItem.as(node);
item.updateOnNextRepaint();
repaintView();
/*
* Cancel editing if the updated geo element has been edited, but
* not otherwise because editing geos while animation is running
* won't work then (ticket #151).
*/
if (isEditItem()) {
if (item.getController().isEditing()) {
item.cancelEditing();
}
}
}
GeoGebraProfiler.addAlgebra(System.currentTimeMillis()-start);
}
/**
* Repaints the whole AV tree, item by item.
*/
public void doRepaint() {
Object geo;
// suppose that the add operations have been already done elsewhere
for (int i = 0; i < getItemCount(); i++) {
TreeItem ti = getItem(i);
if (ti instanceof CheckboxTreeItem) {
CheckboxTreeItem.as(ti).repaint();
} else {
geo = getItem(i).getUserObject();
if (geo instanceof GeoElement) {
RadioTreeItem.as(ti).repaint();
} else if (ti.getWidget() instanceof GroupHeader) {
((GroupHeader) ti.getWidget())
.setText(ti.getUserObject().toString());
if (ti.getState()) {
repaintChildren(ti);
}
}
}
}
if (getInputTreeItem() != null) {
getInputTreeItem().setPixelRatio(app.getPixelRatio());
}
}
/**
* updates only GeoNumerics; used for animated
*/
protected void doRepaintSliders() {
switch (treeMode) {
case ORDER:
repaintSlidersOrder();
break;
case VIEW:
case TYPE:
case LAYER:
for (int i = 0; i < getItemCount(); i++) {
repaintSlidersDependent(getItem(i));
}
break;
case DEPENDENCY:
repaintSlidersDependent(this.indNode);
break;
default:
break;
}
}
private static void repaintSlidersDependent(TreeItem ti) {
if (ti != null) {
for (int j = 0; j < ti.getChildCount(); j++) {
repaintSliderNode(ti.getChild(j));
}
}
}
private void repaintSlidersOrder() {
for (int j = 0; j < getItemCount(); j++) {
repaintSliderNode(getItem(j));
}
}
private static void repaintSliderNode(TreeItem ti) {
if (ti instanceof SliderTreeItemInterface) {
RadioTreeItem.as(ti).repaint();
}
}
private static void repaintChildren(TreeItem item) {
for (int j = 0; j < item.getChildCount(); j++) {
if (item.getChild(j) instanceof RadioTreeItem) {
RadioTreeItem.as(item.getChild(j)).repaint();
}
}
}
private void updateCollapsedNodesIndices() {
// no collapsed nodes
if (getTreeMode() == SortMode.ORDER) {
collapsedNodes = null;
return;
}
if (collapsedNodes == null) {
collapsedNodes = new ArrayList<Integer>();
} else {
collapsedNodes.clear();
}
for (int i = 0; i < getItemCount(); i++) {
TreeItem node = getItem(i);
if (!node.getState()) {
collapsedNodes.add(i);
}
}
}
/**
* @return The display mode of the tree, see MODE_DEPENDENCY, MODE_TYPE
*/
@Override
public SortMode getTreeMode() {
return treeMode;
}
/**
* @param value
* Either AlgebraView.MODE_DEPDENCY or AlgebraView.MODE_TYPE
*/
@Override
public void setTreeMode(SortMode value) {
if (getTreeMode().equals(value)) {
return;
}
clearView();
this.treeMode = value;
initModel();
kernel.notifyAddAll(this);
setLabels();
}
/**
* returns settings in XML format
*
* @param sb
* string builder
*/
public final void getXML(StringBuilder sb) {
if (sbXML == null) {
sbXML = new StringBuilder();
} else {
sbXML.setLength(0);
}
// tree mode
if (getTreeMode() != SortMode.TYPE) {
sbXML.append("\t<mode ");
sbXML.append("val=\"");
sbXML.append(getTreeMode().toInt());
sbXML.append("\"");
sbXML.append("/>\n");
}
// auxiliary objects
boolean flag = showAuxiliaryObjects();
if (flag) {
sbXML.append("\t<auxiliary ");
sbXML.append("show=\"");
sbXML.append(flag);
sbXML.append("\"");
sbXML.append("/>\n");
}
// collapsed nodes
updateCollapsedNodesIndices();
if (collapsedNodes != null && collapsedNodes.size() > 0) {
sbXML.append("\t<collapsed ");
sbXML.append("val=\"");
sbXML.append(collapsedNodes.get(0));
for (int i = 1; i < collapsedNodes.size(); i++) {
sbXML.append(",");
sbXML.append(collapsedNodes.get(i));
}
sbXML.append("\"");
sbXML.append("/>\n");
}
if (sbXML.length() > 0) {
sb.append("<algebraView>\n");
sb.append(sbXML);
sb.append("</algebraView>\n");
}
}
private ArrayList<Integer> collapsedNodes;
private boolean isShowingAuxiliaryObjects;
private void setCollapsedNodes(int[] collapsedNodes) {
if (collapsedNodes == null) {
return;
}
if (this.collapsedNodes == null) {
this.collapsedNodes = new ArrayList<Integer>();
} else {
this.collapsedNodes.clear();
}
for (int i = 0; i < collapsedNodes.length; i++) {
this.collapsedNodes.add(collapsedNodes[i]);
}
}
/**
* @return whether auxiliary objects are showing
*/
private boolean showAuxiliaryObjects() {
return app.showAuxiliaryObjects;
}
/**
* @param flag
* whether to show auxiliary
*/
public void setShowAuxiliaryObjects(boolean flag) {
if (this.isShowingAuxiliaryObjects == flag) {
return;
}
isShowingAuxiliaryObjects = flag;
app.showAuxiliaryObjects = flag;
cancelEditItem();
if (flag) {
clearView();
switch (getTreeMode()) {
default:
// do nothing
break;
case DEPENDENCY:
addItem(auxiliaryNode);
break;
}
kernel.notifyAddAll(this);
} else {
// if we're listing the auxiliary objects in a single leaf we can
// just remove that leaf, but for type-based categorization those
// auxiliary nodes might be scattered across the whole tree,
// therefore we just rebuild the tree
switch (getTreeMode()) {
default:
clearView();
kernel.notifyAddAll(this);
break;
case DEPENDENCY:
if (auxiliaryNode.getParentItem() != null) {
removeItem(auxiliaryNode);
}
break;
}
}
}
/**
* apply the settings
*/
public void applySettings() {
if (!settingsChanged) {
// that means that no settings were stored in the file: reset
// settings to have default
AlgebraSettings settings = app.getSettings().getAlgebra();
settings.reset();
settingsChanged(settings);
}
settingsChanged = false;
// auxilliary objects
setShowAuxiliaryObjects(showAuxiliaryObjectsSettings);
// collapsed nodes
if (collapsedNodes == null) {
return;
}
for (int i : collapsedNodes) {
TreeItem node = getItem(i);
if(node != null){
node.setState(false);
if (node.getWidget() instanceof GroupHeader) {
((GroupHeader) node.getWidget()).setChecked(false);
}
}
}
}
@Override
public void settingsChanged(AbstractSettings settings) {
AlgebraSettings algebraSettings = (AlgebraSettings) settings;
setTreeMode(algebraSettings.getTreeMode());
showAuxiliaryObjectsSettings = algebraSettings
.getShowAuxiliaryObjects();
setCollapsedNodes(algebraSettings.getCollapsedNodes());
settingsChanged = true;
}
/** whether it's attached to kernel */
protected boolean attached = false;
/**
* Fill this view and attach it to kernel
*/
public void attachView() {
if (attached) {
return;
}
attached = true;
clearView();
kernel.notifyAddAll(this);
applySettings();
kernel.attach(this);
/*
* if (treeMode == SortMode.DEPENDENCY) { indNode.setState(true);
* depNode.setState(true); if (auxiliaryNode.getParentItem() != null) {
* auxiliaryNode.setState(true); } }
*/
}
/**
* Detach this from kernel
*/
public void detachView() {
kernel.detach(this);
clearView();
attached = false;
}
/**
* Method to initialize the tree model of the current tree mode. This method
* should be called whenever the tree mode is changed, it won't initialize
* anything if not necessary.
*
* This method will also actually change the model of the tree.
*/
protected void initModel() {
// build default tree structure
switch (treeMode) {
default:
case DEPENDENCY:
// don't re-init anything
if (depNode == null || indNode == null || auxiliaryNode == null) {
// rootDependency = new TreeItem();
depNode = new AVTreeItem(); // dependent objects
indNode = new AVTreeItem();
auxiliaryNode = new AVTreeItem();
}
// set the root
clear();
addItem(indNode);
addItem(depNode);
// add auxiliary node if neccessary
if (app.showAuxiliaryObjects) {
if (auxiliaryNode.getTree() != this) {
addItem(auxiliaryNode);
}
}
break;
case ORDER:
if (rootOrder == null) {
// both rootOrder and AlgebraView will have the Tree items
rootOrder = new AVTreeItem();
}
setUserObject(rootOrder, "", "");
// always try to remove the auxiliary node
if (app.showAuxiliaryObjects && auxiliaryNode != null) {
removeAuxiliaryNode();
}
// set the root
clear();
if (isAlgebraInputVisible()) {
// why is this here and not in case of DEPENDENCY, LAYER
super.addItem(inputPanelTreeItem);
}
break;
case TYPE:
// don't re-init anything
if (rootType == null) {
rootType = new AVTreeItem();
// setUserObject(rootType, "");
typeNodesMap = new HashMap<String, TreeItem>(5);
}
// always try to remove the auxiliary node
if (app.showAuxiliaryObjects && auxiliaryNode != null) {
removeAuxiliaryNode();
}
// set the root
clear();
if (isAlgebraInputVisible()) {
// why is this here and not in case of DEPENDENCY, LAYER
super.addItem(inputPanelTreeItem);
}
break;
case LAYER:
// don't re-init anything
if (rootLayer == null) {
rootLayer = new AVTreeItem();
layerNodesMap = new HashMap<Integer, TreeItem>(10);
}
// always try to remove the auxiliary node
if (app.showAuxiliaryObjects && auxiliaryNode != null) {
removeAuxiliaryNode();
}
// set the root
clear();
// addItem(rootLayer);
break;
}
}
/**
* remove all from the tree
*/
protected void clearTree() {
switch (getTreeMode()) {
default:
case DEPENDENCY:
indNode.removeItems();
depNode.removeItems();
auxiliaryNode.removeItems();
break;
case TYPE:
removeItems();
typeNodesMap.clear();
break;
case LAYER:
removeItems();
layerNodesMap.clear();
break;
case ORDER:
rootOrder.removeItems();
removeItems();
}
}
/**
* set labels on the tree
*/
protected void setTreeLabels() {
TreeItem node;
switch (getTreeMode()) {
case DEPENDENCY:
setUserObject(indNode, loc.getPlain("FreeObjects"), "1");
setUserObject(depNode, loc.getPlain("DependentObjects"), "2");
setUserObject(auxiliaryNode, loc.getPlain("AuxiliaryObjects"), "3");
break;
case TYPE:
for (Entry<String, TreeItem> entry : typeNodesMap.entrySet()) {
String key = entry.getKey();
node = entry.getValue();
setUserObject(node, loc.getMenu(key), key);
}
break;
case LAYER:
for (Entry<Integer, TreeItem> entry : layerNodesMap.entrySet()) {
Integer key = entry.getKey();
node = entry.getValue();
setUserObject(node, loc.getPlain("LayerA", key.toString()),
key.toString());
}
break;
case ORDER:
break;
}
// if (lastLang == null || !lastLang.equals(loc.getLocaleStr())) {
rebuildItems();
// }
}
/**
*
* @param geo
* element
* @param forceLayer
* override layer stored in Geo
* @return parent node of this geo
*/
protected TreeItem getParentNode(GeoElement geo, int forceLayer) {
TreeItem parent;
switch (treeMode) {
case DEPENDENCY:
if (geo.isAuxiliaryObject()) {
parent = auxiliaryNode;
} else if (geo.isIndependent()) {
parent = indNode;
} else {
parent = depNode;
}
break;
case TYPE:
// get type node
String typeString = geo.getTypeStringForAlgebraView();
parent = typeNodesMap.get(typeString);
// do we have to create the parent node?
if (parent == null) {
String transTypeString = geo
.translatedTypeStringForAlgebraView();
parent = new AVTreeItem(new InlineLabel(transTypeString));
setUserObject(parent, transTypeString, typeString);
typeNodesMap.put(typeString, parent);
// find insert pos
int pos = getItemCount();
for (int i = 0; i < pos; i++) {
TreeItem child = getItem(i);
String groupName = getGroupName(child);
if (typeString.compareTo(groupName) < 0
||
(child.getWidget() != null
&& this.inputPanelTreeItem != null
&& this.inputPanelTreeItem.getWidget() != null
&& child.getWidget().equals(this.inputPanelTreeItem.getWidget()))) {
pos = i;
break;
}
}
insertItem(pos, parent);
}
break;
case LAYER:
// get type node
int layer = forceLayer > -1 ? forceLayer : geo.getLayer();
parent = layerNodesMap.get(layer);
// do we have to create the parent node?
if (parent == null) {
String layerStr = loc.getPlain("LayerA", layer + "");
parent = new AVTreeItem(new InlineLabel(layerStr));
setUserObject(parent, layerStr, layer + "");
layerNodesMap.put(layer, parent);
// find insert pos
int pos = getItemCount();
for (int i = 0; i < pos; i++) {
TreeItem child = getItem(i);
if (layerStr.compareTo(getGroupName(child)) < 0) {
pos = i;
break;
}
}
insertItem(pos, parent);
}
break;
case ORDER:
parent = rootOrder;
break;
default:
parent = null;
}
return parent;
}
private static String getGroupName(TreeItem child) {
return child.getUserObject() instanceof String
? ((String) child.getUserObject()) : "_";
}
/**
* Assign element or element group to a given tree node
*
* @param ti
* tree item
* @param ob
* object
* @param key
* sorting key
*/
public final void setUserObject(TreeItem ti, final String ob, String key) {
ti.setUserObject(ob);
GroupHeader group = new GroupHeader(this.app.getSelectionManager(), ti,
ob, key,
GuiResources.INSTANCE.algebra_tree_open().getSafeUri(),
GuiResources.INSTANCE.algebra_tree_closed().getSafeUri());
group.getElement().getStyle().setFontSize(app.getFontSizeWeb(),
Unit.PX);
ti.setWidget(group);
}
/**
* @param ob
* geo element
* @param forceRetex
* whether ReTeX editor should be used
* @return AV item
*/
public final static RadioTreeItem createAVItem(final GeoElement ob,
boolean forceRetex) {
RadioTreeItem ti = null;
if (SliderTreeItemRetex.match(ob)) {
if (forceRetex) {
ti = new SliderTreeItemRetex(ob);
} else {
ti = new ReTeXHelper()
.getSliderItem(ob);
}
} else if (CheckboxTreeItem.match(ob)) {
if (forceRetex) {
ti = new CheckboxTreeItem(ob);
} else {
ti = new ReTeXHelper()
.getCheckboxItem(ob);
}
} else if (forceRetex) {
ti = new LatexTreeItem(ob);
} else {
ti = new ReTeXHelper().getAVItem(ob);
}
ti.setUserObject(ob);
ti.addStyleName("avItem");
return ti;
}
/**
* Remove this node from the model.
*
* @param node
* @param model
*/
private void removeFromModel(TreeItem node) {
node.remove();
nodeTable.remove(node.getUserObject());
// remove the type branch if there are no more children
switch (treeMode) {
case DEPENDENCY:
default:
// do nothing
break;
case TYPE:
String typeString = ((GeoElement) node.getUserObject())
.getTypeStringForAlgebraView();
TreeItem parent = typeNodesMap.get(typeString);
// this has been the last node
if (parent != null && parent.getChildCount() == 0) {
typeNodesMap.remove(typeString);
parent.remove();
}
break;
case LAYER:
removeFromLayer(((GeoElement) node.getUserObject()).getLayer());
break;
case ORDER:
rootOrder.removeItem(node);
if (getItemCount() > 0 && getItem(0) instanceof RadioTreeItem) {
((RadioTreeItem) getItem(0)).setFirst(true);
}
}
}
private void removeFromLayer(int i) {
TreeItem parent = layerNodesMap.get(i);
// this has been the last node
if ((parent != null) && parent.getChildCount() == 0) {
layerNodesMap.remove(i);
parent.remove();
}
}
private void addRadioTreeItem(TreeItem parent, RadioTreeItem node) {
// add node to model (alphabetically ordered)
int pos = getInsertPosition(parent, node.geo, treeMode);
if (pos == 0 && parent == rootOrder) {
node.setFirst(true);
}
if (pos >= parent.getChildCount()) {
if (treeMode == SortMode.LAYER) {
if (isAlgebraInputVisible()) {
removeItem(inputPanelTreeItem);
}
parent.addItem(node);
if (isAlgebraInputVisible()) {
super.addItem(inputPanelTreeItem);
}
} else {
parent.addItem(node);
if (parent.equals(rootOrder)) {
addItem(node);
}
}
} else {
try {
parent.insertItem(pos, node);
if (parent.equals(rootOrder)) {
insertItem(pos, node);
}
} catch (IndexOutOfBoundsException e) {
parent.addItem(node);
if (parent.equals(rootOrder)) {
addItem(node);
}
}
}
}
/**
* adds a new node to the tree
*/
@Override
public void add(GeoElement geo) {
add(geo, -1);
}
private void add(GeoElement geo, int forceLayer) {
if (!this.isAttachedToKernel()) {
return;
}
cancelEditItem();
this.isShowingAuxiliaryObjects = showAuxiliaryObjects();
if (geo.isLabelSet() && geo.showInAlgebraView()
&& geo.isSetAlgebraVisible()) {
// don't add auxiliary objects if the tree is categorized by type
if (!getTreeMode().equals(SortMode.DEPENDENCY)
&& !showAuxiliaryObjects() && geo.isAuxiliaryObject()) {
return;
}
TreeItem parent = getParentNode(geo, forceLayer);
RadioTreeItem node = createAVItem(geo,
true);
addRadioTreeItem(parent, node);
// if (node != null && !node.isInputTreeItem()) {
// setActiveTreeItem(node);
// }
RadioTreeItem.as(node).setItemWidth(getMaxItemWidth());
boolean wasEmpty = isNodeTableEmpty();
nodeTable.put(geo, node);
if (wasEmpty) {
// if adding new elements the first time,
// let's show the X signs in the input bar!
if (this.inputPanelLatex != null) {
this.inputPanelLatex.updateGUIfocus(inputPanelLatex, false);
}
}
// ensure that the leaf with the new object is visible
parent.setState(true);
}
if (inputPanelLatex != null) {
inputPanelLatex.styleScrollBox();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
inputPanelLatex.updateButtonPanelPosition();
getAlgebraDockPanel().scrollToBottom();
}
});
}
}
@Override
public void changeLayer(GeoElement g, int oldLayer, int newLayer) {
if (this.treeMode.equals(SortMode.LAYER)) {
TreeItem node = nodeTable.get(g);
if (node != null) {
node.remove();
nodeTable.remove(node.getUserObject());
removeFromLayer(oldLayer);
}
this.add(g, newLayer);
}
}
/**
* removes a node from the tree
*/
@Override
public void remove(GeoElement geo) {
cancelEditItem();
TreeItem node = nodeTable.get(geo);
if (node != null) {
removeFromModel(node);
}
if (inputPanelLatex != null) {
inputPanelLatex.updateButtonPanelPosition();
}
}
@Override
public void clearView() {
nodeTable.clear();
latexLoaded = false;
clearTree();
activeItem = null;
if (inputPanelLatex != null) {
inputPanelLatex.setText("");
}
showAlgebraInput(false);
}
/**
* renames an element and sorts list
*/
@Override
public void rename(GeoElement geo) {
remove(geo);
add(geo);
}
private void removeAuxiliaryNode() {
removeItem(auxiliaryNode);
}
/**
* Gets the insert position for newGeo to insert it in alphabetical order in
* parent node. Note: all children of parent must have instances of
* GeoElement as user objects.
*
* @param parent
* parent node
* @param newGeo
* geo to be inserted
*
* @param mode
* sort mode
* @return position
*/
final public int getInsertPosition(TreeItem parent,
GeoElement newGeo, SortMode mode) {
// label of inserted geo
// String newLabel = newGeo.getLabel();
// standard case: binary search
int left = 0;
int right = parent == rootOrder ? getItemCount() : parent
.getChildCount();
if (right == 0) {
return right;
}
// bigger then last?
TreeItem node = parent == rootOrder ? getItem(right - 1) : parent
.getChild(right - 1);
// String nodeLabel = ((GeoElement) node.getUserObject()).getLabel();
if (node.getUserObject() == null) {
if (right == 1) {
return 0;
}
node = parent == rootOrder ? getItem(right - 2) : parent
.getChild(right - 2);
right--;
}
GeoElement geo2 = ((GeoElement) node.getUserObject());
if (compare(newGeo, geo2, mode)) {
return right;
}
// binary search
while (right > left) {
int middle = (left + right) / 2;
node = parent == rootOrder ? getItem(middle) : parent
.getChild(middle);
// nodeLabel = ((GeoElement) node.getUserObject()).getLabel();
geo2 = ((GeoElement) node.getUserObject());
if (!compare(newGeo, geo2, mode)) {
right = middle;
} else {
left = middle + 1;
}
}
// insert at correct position
return right;
}
/**
* Performs a binary search for geo among the children of parent. All
* children of parent have to be instances of GeoElement sorted
* alphabetically by their names.
*
* @param parent
* parent node
* @param geoLabel
* label of geo
*
* @return -1 when not found
*/
final public static int binarySearchGeo(TreeItem parent, String geoLabel) {
int left = 0;
int right = parent.getChildCount() - 1;
if (right == -1 || geoLabel == null) {
return -1;
}
// binary search for geo's label
while (left <= right) {
int middle = (left + right) / 2;
TreeItem node = parent.getChild(middle);
String nodeLabel = ((GeoElement) node.getUserObject())
.getLabelSimple();
int compare = GeoElement.compareLabels(geoLabel, nodeLabel);
if (compare < 0) {
right = middle - 1;
} else if (compare > 0) {
left = middle + 1;
} else {
return middle;
}
}
return -1;
}
/**
* Performs a linear search for geo among the children of parent.
*
* @param parent
* parent node
* @param geoLabel
* label of geo
* @return -1 when not found
*/
final public static int linearSearchGeo(TreeItem parent, String geoLabel) {
if (geoLabel == null) {
return -1;
}
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
TreeItem node = parent.getChild(i);
GeoElement g = (GeoElement) node.getUserObject();
if (geoLabel.equals(g.getLabel(StringTemplate.defaultTemplate))) {
return i;
}
}
return -1;
}
/**
* Reset the algebra view if the mode changed.
*/
@Override
public void setMode(int mode, ModeSetter m) {
reset();
}
private static boolean compare(GeoElement geo1, GeoElement geo2,
SortMode mode) {
switch (mode) {
case ORDER:
return geo1.getConstructionIndex() > geo2.getConstructionIndex();
default: // alphabetical
return GeoElement.compareLabels(
geo1.getLabel(StringTemplate.defaultTemplate),
geo2.getLabel(StringTemplate.defaultTemplate)) > 0;
}
}
@Override
final public void updateAuxiliaryObject(GeoElement geo) {
remove(geo);
add(geo);
}
@Override
public void reset() {
cancelEditItem();
repaintView();
ensureSelectedItemVisible();
}
/**
* resets all fix labels of the View. This method is called by the
* application if the language setting is changed.
*/
@Override
public void setLabels() {
/*
* if (inputPanel != null) { inputPanel.setLabels(); }
*/
// TODO add button ?
setTreeLabels();
if (this.inputPanelLatex != null) {
this.inputPanelLatex.setLabels();
}
if (this.styleBar != null) {
this.styleBar.setLabels();
}
if (inputPanelLatex != null && inputPanelLatex.hasHelpPopup()) {
app.getGuiManager().getInputHelpPanel().setLabels();
}
}
@Override
public final GeoElement getLastSelectedGeo() {
return getSelectionCtrl().getLastSelectedGeo();
}
@Override
public final void setLastSelectedGeo(GeoElement geo) {
getSelectionCtrl().setLastSelectedGeo(geo);
}
@Override
public void startBatchUpdate() {
// TODO Auto-generated method stub
}
@Override
public void endBatchUpdate() {
// TODO Auto-generated method stub
}
private TreeItem inputPanelTreeItem;
/**
* @return the RadioButtonTreeItem containing the input-box
*/
public RadioTreeItem getInputTreeItem() {
return inputPanelLatex;
}
/**
* @return currently edited tree item
*/
public RadioTreeItem getActiveTreeItem() {
if (activeItem == null) {
return this.inputPanelLatex;
}
return this.activeItem;
}
/**
* Create new input panel and add it
*/
public void setInputPanel() {
// usually, inputPanel is here, but not in use (not attached)
boolean forceKeyboard = false;
boolean inputJustCreated = false;
if (inputPanelLatex == null) {
inputPanelLatex = createInputPanel();
forceKeyboard = GuiManagerW.mayForceKeyboard(app);
inputJustCreated = true;
} else {
inputPanelLatex.removeFromParent();
}
hideAlgebraInput();
this.inputPanelTreeItem = new TreeItem(inputPanelLatex.getWidget());
inputPanelTreeItem.addStyleName("avInputItem");
inputPanelLatex.getWidget().getElement().getParentElement()
.addClassName("NewRadioButtonTreeItemParent");
inputPanelLatex.replaceXButtonDOM();
unselect(getSelectionCtrl().getSelectedGeo());
unselect(getSelectionCtrl().getLastSelectedGeo());
if (inputJustCreated) {
if (isNodeTableEmpty()) {
inputPanelLatex.updateGUIfocus(inputPanelLatex, false);
}
}
showAlgebraInput(forceKeyboard);
}
private RadioTreeItem createInputPanel() {
return new LatexTreeItem(kernel);
}
@Override
public void setShowAlgebraInput(boolean show) {
if (show) {
showAlgebraInput(false);
} else {
hideAlgebraInput();
}
}
private void hideAlgebraInput() {
if (!isAlgebraInputVisible()) {
return;
}
super.removeItem(inputPanelTreeItem);
inputPanelTreeItem = null;
}
private void showAlgebraInput(boolean forceKeyboard0) {
if (!app.showAlgebraInput() || app.getInputPosition() != InputPosition.algebraView) {
hideAlgebraInput();
return;
}
Integer inputWidth = null;
if (isAlgebraInputVisible()) {
// note that hideAlgebraInput just does this
// hideAlgebraInput();
// except it also makes this null, no problem
// ... or? still preferring to be safe
inputWidth = inputPanelLatex.getWidget().getElement()
.getParentElement().getClientWidth();
super.removeItem(inputPanelTreeItem);
// inputPanel.removeFromParent();//?
}
boolean inputJustCreated = false;
boolean forceKeyboard = false;
boolean suggestKeyboard = false;
if (inputPanelLatex == null) {
suggestKeyboard = true;
forceKeyboard = forceKeyboard0 || GuiManagerW.mayForceKeyboard(app);
inputPanelLatex = createInputPanel();
// open the keyboard (or show the keyboard-open-button) at
// when the application is started
inputJustCreated = true;
} else {
inputPanelLatex.removeFromParent();
}
inputPanelTreeItem = super.addItem(inputPanelLatex.getWidget());
inputPanelTreeItem.addStyleName("avInputItem");
// inputPanelTreeItem.addStyleName("NewRadioButtonTreeItemParent");
inputPanelLatex.getWidget().getElement().getParentElement()
.addClassName("NewRadioButtonTreeItemParent");
inputPanelLatex.replaceXButtonDOM();
if (inputJustCreated) {
if (isNodeTableEmpty()) {
inputPanelLatex.updateGUIfocus(inputPanelLatex, false);
}
}
if ((!app.getLocalization().getLanguage().equals("ko") || app
.has(Feature.KOREAN_KEYBOARD))
&& app.showView(App.VIEW_ALGEBRA)) {
if (forceKeyboard) {
doShowKeyboard();
}else if(suggestKeyboard){
app.getAppletFrame().showKeyboardOnFocus();
}
}
if (inputWidth != null) {
inputPanelLatex.setItemWidth(inputWidth);
}
updateFonts();
}
private void doShowKeyboard() {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
setActiveTreeItem(null);
app.showKeyboard(inputPanelLatex, true);
}
});
}
private boolean isAlgebraInputVisible() {
return inputPanelTreeItem != null;
}
@Override
public void addItem(TreeItem item) {
// make sure the item is inserted before the inputPanel
if(isAlgebraInputVisible()){
removeItem(inputPanelTreeItem);
}
super.addItem(item);
if (isAlgebraInputVisible()) {
super.addItem(inputPanelTreeItem);
}
}
@Override
protected boolean isKeyboardNavigationEnabled(TreeItem ti) {
// keys should move the geos in the EV
// if (isEditing())
return false;
// return super.isKeyboardNavigationEnabled(ti);
}
/**
* @return true if nodeTable is empty
*/
public boolean isNodeTableEmpty() {
return this.nodeTable.isEmpty();
}
/**
* @return number of elements in the view
*/
public int getNodeTableSize() {
return this.nodeTable.size();
}
/**
* @param item
* new active item
*/
public void setActiveTreeItem(RadioTreeItem item) {
if (item == null) {
getSelectionCtrl().clear();
}
boolean sameItem = activeItem == item;
if (sameItem) {
return;
}
stopCurrentEditor();
if ((this.activeItem != null)
&& (!this.activeItem.commonEditingCheck())
&& !app.has(Feature.AV_SINGLE_TAP_EDIT)) {
// removeCloseButton() on this would cause infinite recursion
activeItem.removeCloseButton();
}
if (activeItem != null
&& !selectionCtrl.isMultiSelect()) {
selectRow(activeItem.getGeo(), false);
}
this.activeItem = item;
//
if (activeItem != null) {
selectRow(activeItem.getGeo(), true);
}
}
private void stopCurrentEditor() {
if (getActiveTreeItem() != null) {
if (app.has(Feature.AV_SINGLE_TAP_EDIT)) {
getActiveTreeItem().onEnter(false);
} else if (app.has(Feature.AV_INPUT_BUTTON_COVER)) {
getActiveTreeItem().stopEditing(null, null);
} else {
getActiveTreeItem().onEnter(false);
}
}
}
/**
* Remove close button from active item
*/
public void removeCloseButton() {
if (activeItem != null) {
activeItem.removeCloseButton();
stopCurrentEditor();
// use setter to make sure current item is reset correctly
activeItem = null;
}
}
/**
* @param geo
* geo to be (un)selected
* @param select
* whether to select or unselect
*/
public void selectRow(GeoElement geo, boolean select) {
TreeItem node = nodeTable.get(geo);
if (node == null) {
return;
}
RadioTreeItem.as(node).selectItem(select);
}
/**
* @param mayCreate
* true if this may call constructor (otherwise it may return
* null)
* @return {@link AlgebraStyleBarW}
*/
public AlgebraStyleBarW getStyleBar(boolean mayCreate) {
if (mayCreate && this.styleBar == null) {
this.styleBar = new AlgebraStyleBarW(app);
}
return this.styleBar;
}
@Override
public void updateVisualStyle(GeoElement geo, GProperty prop) {
update(geo);
if (styleBar != null && geo.isLabelSet()) {
styleBar.update(geo);
}
}
@Override
public void updatePreviewFromInputBar(GeoElement[] geos) {
if (geos != null) {
for (GeoElement geo : geos) {
inputPanelLatex.previewValue(geo);
}
} else {
inputPanelLatex.clearPreview();
}
}
@Override
public boolean isShowing() {
return isVisible() && isAttached();
}
@Override
public void cancelEditItem() {
editItem = false;
setAnimationEnabled(true);
}
@Override
public boolean isEditItem() {
return editItem;
}
/**
* Starts the corresponding editor for geo.
*/
@Override
public void startEditItem(GeoElement geo) {
if (geo == null) {
editItem = true;
return;
}
// open Object Properties for eg GeoImages
// also for GeoPenStroke
if (!geo.isAlgebraViewEditable()) {
ArrayList<GeoElement> geos = new ArrayList<GeoElement>();
geos.add(geo);
app.getDialogManager().showPropertiesDialog(geos);
return;
}
getAlgebraDockPanel().saveScrollPosition();
if (!geo.isPointOnPath() && !geo.isPointInRegion()) {
if ((!geo.isIndependent() && !(geo.getParentAlgorithm() instanceof AlgoCurveCartesian))
|| !attached) // needed for F2 when Algebra
// View closed
{
if (geo.isRedefineable()) {
redefine(geo);
}
return;
}
if (!geo.isChangeable()) {
if (geo.isProtected(EventType.UPDATE)) {
app.showMessage(loc.getError("AssignmentToFixed"));
return;
} else if (geo.isRedefineable()
&& !(geo.getParentAlgorithm() instanceof AlgoCurveCartesian)) {
redefine(geo);
return;
} else if (!(geo.getParentAlgorithm() instanceof AlgoCurveCartesian)) {
return;
}
}
}
TreeItem node = nodeTable.get(geo);
if (node != null) {
cancelEditItem();
editItem = true;
setAnimationEnabled(false);
if (node instanceof RadioTreeItem) {
RadioTreeItem ri = RadioTreeItem.as(node);
expandAVToItem(ri);
ri.enterEditMode(geo.isPointOnPath() || geo.isPointInRegion());
}
}
}
/**
* Expand AV to make space for editing a specific item
*
* @param ri
* edited item
*/
void expandAVToItem(RadioTreeItem ri) {
int editedWidth = ri.getWidthForEdit();
int expanded = editedWidth;
if (editedWidth < userWidth) {
expanded = userWidth;
} else {
expanded = editedWidth;
}
expandWidth(expanded);
setWidths(expanded);
}
private void redefine(GeoElement geo) {
if (geo.getParentAlgorithm() instanceof AlgoDependentText) {
app.getDialogManager().showRedefineDialog(geo, true);
return;
}
TreeItem node = nodeTable.get(geo);
if (node != null) {
cancelEditItem();
// FIXMEWEB select and show node
editItem = true;
setAnimationEnabled(false);
if (node instanceof RadioTreeItem) {
RadioTreeItem ri = RadioTreeItem.as(node);
expandAVToItem(ri);
if (!ri.enterEditMode(false)) {
cancelEditItem();
app.getDialogManager().showRedefineDialog(geo, true);
}
}
}
}
/**
* Clear selection
*/
public void clearSelection() {
// deselecting this causes a bug; it maybe fixed
// by changing the repaintView method too,
// adding setSelectedItem( some TreeItem ),
// but which TreeItem should be that if more are selected?
// that's why Arpad choosed to comment this out instead
// super.setSelectedItem(null);
for (int i = 0; i < getItemCount(); i++) {
if (!(getItem(i).getUserObject() instanceof GeoElement)) {
for (int j = 0; j < getItem(i).getChildCount(); j++) {
TreeItem item = getItem(i).getChild(j);
item.setSelected(false);
if (item instanceof RadioTreeItem) {
unselect(RadioTreeItem.as(item).getGeo());
}
}
}
}
getSelectionCtrl().setSelectedGeo(null);
}
/**
* @return selected geo
*/
public GeoElement getSelectedGeoElement() {
return getSelectionCtrl().getSelectedGeo();
}
/**
*
* @param event
* drag event
* @param geo
* element
*/
public void dragStart(DragStartEvent event, GeoElement geo) {
setDraggedGeo(geo);
}
@Override
public GeoElement getDraggedGeo() {
return draggedGeo;
}
private void setDraggedGeo(GeoElement draggedGeo) {
this.draggedGeo = draggedGeo;
}
@Override
public boolean hasFocus() {
Log.debug("unimplemented");
return false;
}
@Override
protected void onLoad() {
// this may be important if the view is added/removed from the DOM
super.onLoad();
repaintView();
}
private int mqFontSize = -1;
private int maxItemWidth = 0;
private boolean latexLoaded;
private int userWidth;
/*
* private int resizedWidth;* Not used in Web so far. Will not work with
* JLM.
*/
private void updateFonts() {
if (mqFontSize != app.getFontSizeWeb()) {
mqFontSize = app.getFontSizeWeb();
new ReTeXHelper()
.setFontSize(mqFontSize);
if (getInputTreeItem() != null) {
getInputTreeItem().updateFonts();
}
}
}
/**
* @param ratio
* pixel ratio
*/
public void setPixelRatio(double ratio) {
updateFonts();
rebuildItems();
}
private void rebuildItems() {
for (int i = 0; i < getItemCount(); i++) {
TreeItem ti = getItem(i);
if (ti instanceof RadioTreeItem) {
RadioTreeItem.as(ti).updateOnNextRepaint();
} else if (ti.getWidget() instanceof GroupHeader) {
ti.getWidget().getElement().getStyle()
.setFontSize(app.getFontSizeWeb(), Unit.PX);
for (int j = 0; j < ti.getChildCount(); j++) {
if (ti.getChild(j) instanceof RadioTreeItem) {
RadioTreeItem.as(ti.getChild(j))
.updateOnNextRepaint();
}
}
}
}
this.repaintView();
}
/**
* Update items for new window size / pixel ratio
*/
public void resize() {
if (app.has(Feature.AV_SINGLE_TAP_EDIT)) {
// setOriginalWidth(null);
}
int resizedWidth = getOffsetWidth();
if (maxItemWidth == 0) {
maxItemWidth = resizedWidth;
}
if (resizedWidth > maxItemWidth) {
maxItemWidth = resizedWidth;
}
setWidths(resizedWidth);
if (activeItem != null) {
activeItem.updateButtonPanelPosition();
}
}
/**
* AV DockPanel resizing rules:
*
* maxItemWith: the longest item width.
*
* avWidth: the current width of AV dock panel.
*
* userWidth: the width that user sets using the splitter.
*
* editedWidth: width of the current edited item.
*
*
* width of InputTreeItem must be maxItemWidth when it is not on focus, to
* preserve its 'blue line' border. On focus its width must be avWidth
* userWidth should change only if user changes it.
*
* Expanding AV dock panel
*
* When user starts to edit an item, AV dock panel should be resized to:
*
* - avWidth: if the avWidth is less than the edited item's width
*
* - userWidth: if userWidth is greater than both editedWidth and avWidth
*
*/
/**
* Sets each tree item to a specific width
*
* @param width
* to set.
*/
public void setItemWidth(int width) {
if (width > maxItemWidth) {
maxItemWidth = width;
}
setWidths(maxItemWidth);
}
private void setWidths(int width) {
if (this.getInputTreeItem() != null) {
getInputTreeItem().setItemWidth(width);
getInputTreeItem().reposition();
}
for (int i = 0; i < getItemCount(); i++) {
TreeItem ti = getItem(i);
if (ti instanceof RadioTreeItem) {
RadioTreeItem ri = RadioTreeItem.as(ti);
ri.setItemWidth(width);
} else if (ti.getWidget() instanceof GroupHeader) {
for (int j = 0; j < ti.getChildCount(); j++) {
if (ti.getChild(j) instanceof RadioTreeItem) {
RadioTreeItem.as(ti.getChild(j))
.setItemWidth(width);
}
}
}
}
}
/**
* Update highlighting of rows
*/
public void updateSelection() {
// if (selectionCtrl.isMultiSelect()) {
// return;
// }
if (selectionCtrl.isEmpty()) {
removeCloseButton();
}
for (int i = 0; i < getItemCount(); i++) {
TreeItem ti = getItem(i);
if (ti instanceof RadioTreeItem) {
GeoElement geo = RadioTreeItem.as(ti).getGeo();
if (geo != null) {
selectRow(geo, geo.doHighlighting());
}
} else if (ti.getWidget() instanceof GroupHeader) {
for (int j = 0; j < ti.getChildCount(); j++) {
if (ti.getChild(j) instanceof RadioTreeItem) {
GeoElement geo = RadioTreeItem.as(ti.getChild(j))
.getGeo();
if (geo != null) {
selectRow(geo, geo.doHighlighting());
}
}
}
}
}
}
/**
* Clears the selection of the last selected item, it also stops editing if
* it is currently edited.
*/
public void unselectActiveItem() {
if (activeItem != null) {
activeItem.getController().stopEdit();
unselect(activeItem.getGeo());
}
// repaintView();
}
/**
* Reset active item to null
*/
public void clearActiveItem() {
activeItem = null;
// repaintView();
}
private void unselect(GeoElement geo) {
if (geo == null) {
return;
}
TreeItem node = nodeTable.get(geo);
RadioTreeItem.as(node).selectItem(false);
selectRow(geo, false);
}
@Override
public void resetItems(boolean unselectAll) {
MinMaxPanel.closeMinMaxPanel();
if (app.has(Feature.AV_SINGLE_TAP_EDIT)) {
cancelEditItem();
stopCurrentEditor();
restoreWidth(false);
return;
}
updateSelection();
}
/**
* @return selection controller
*/
public AVSelectionController getSelectionCtrl() {
return selectionCtrl;
}
/**
* Gets the original width before AV expansion to restore original width
* after.
*
* @return original width in pixels
*/
public Integer getOriginalWidth() {
return originalWidth;
}
/**
* @param oldWidth
* original width in pixels
*/
public void setOriginalWidth(Integer oldWidth) {
this.originalWidth = oldWidth;
}
@Override
public boolean isAttachedToKernel() {
return attached;
}
private static void addLeaf(HasTreeItems printItem, RadioTreeItem leaf) {
RadioTreeItem printLeaf = leaf.copy();
printItem.addItem(printLeaf);
printLeaf.repaint();
}
@Override
public void getPrintable(FlowPanel pPanel, final Button btPrint) {
Tree printTree = new Tree();
pPanel.clear();
DrawEquationW.setPrintScale(10);
for (int i = 0; i < this.getItemCount(); i++) {
TreeItem item = this.getItem(i);
if (item instanceof RadioTreeItem) {
addLeaf(printTree, (RadioTreeItem) item);
} else if (item != inputPanelTreeItem) {
TreeItem printItem = new TreeItem();
printItem.setText(item.getText());
for (int j = 0; j < item.getChildCount(); j++) {
TreeItem leaf = item.getChild(j);
if (leaf instanceof RadioTreeItem) {
addLeaf(printItem, (RadioTreeItem) leaf);
}
}
printItem.setState(true);
printTree.addItem(printItem);
}
}
DrawEquationW.setPrintScale(1);
pPanel.add(printTree);
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
btPrint.setEnabled(true);
}
});
}
/**
* The maximum width of the items. Relevant for scrolling horizontally.
*
* @return the max item width.
*/
public int getMaxItemWidth() {
int avWidth = getAlgebraDockPanel().getOffsetWidth();
return maxItemWidth < avWidth ? avWidth : maxItemWidth;
}
/**
* Algebra View is expanded to the item's width when editing.
*
* @param width
* The width to expand.
*/
public void expandWidth(int width) {
if (!app.has(Feature.AV_SINGLE_TAP_EDIT)) {
return;
}
AlgebraDockPanelW avDockPanel = getAlgebraDockPanel();
DockSplitPaneW splitPane = avDockPanel.getParentSplitPane();
if (splitPane == null || splitPane
.getOrientation() == SwingConstants.VERTICAL_SPLIT
|| splitPane.isCenter(avDockPanel)) {
return;
}
final int w = width;
splitPane.setWidgetSize(avDockPanel, w);
avDockPanel.deferredOnResize();
}
/**
* Restores AV original size before editing, if it has been expanded.
*
* @param force
* whether to force this over the canceling timer
*/
public void restoreWidth(boolean force) {
if (!force && CancelEventTimer.cancelAVRestoreWidth()) {
return;
}
int w = userWidth;
AlgebraDockPanelW avDockPanel = getAlgebraDockPanel();
DockSplitPaneW avParent = getAlgebraDockPanel().getParentSplitPane();
if (avParent == null || userWidth == 0 || avParent
.getOrientation() == SwingConstants.VERTICAL_SPLIT) {
return;
}
// normally the "center" orientation should be handled by the
// VERTICAL_SPLIT check above
if (!avParent.isCenter(avDockPanel)) {
avParent.setWidgetSize(avDockPanel, w);
avDockPanel.deferredOnResize();
}
}
/**
* @return algebra dock panel
*/
AlgebraDockPanelW getAlgebraDockPanel() {
return (AlgebraDockPanelW) app.getGuiManager().getLayout()
.getDockManager().getPanel(App.VIEW_ALGEBRA);
}
/**
* @return whether active element is the input row
*/
public boolean isInputActive() {
return activeItem == inputPanelLatex;
}
/**
* Flag to determine whether we can render elements as LaTeX synchronously.
* When false, render as text temporarily.
*
* @return whether LaTeX is already loaded
*/
public boolean isLaTeXLoaded() {
return latexLoaded;
}
/**
* Notify that LaTeX was loaded
*/
public void setLaTeXLoaded() {
latexLoaded = true;
}
/**
* Gets the TreeItem associated with the geo.
*
* @param geo
* the element to look for.
* @return the TreeItem with the geo.
*/
public TreeItem getNode(GeoElement geo) {
return nodeTable.get(geo);
}
/**
* @return width determined by user resizing
*/
public int getUserWidth() {
return userWidth;
}
/**
* @param userWidth
* width determined by user resizing
*/
public void setUserWidth(int userWidth) {
MinMaxPanel.closeMinMaxPanel();
this.userWidth = userWidth;
}
/**
* Set AV width to current offset width or default if not attached
*/
public void setDefaultUserWidth() {
int w = getOffsetWidth();
setUserWidth(w > 0 ? w : getDefaultAVWidth());
}
private int getDefaultAVWidth() {
return (int) (app.getWidth()
* PerspectiveDecoder.landscapeRatio(app.getWidth()));
}
}