public void redo() {
			super.redo();
			doNotFireUndoEvent = true;
			switch (undoID) {
			case UndoAction.INSERT_A_PARTICLE:
				if (!model.getRecorderDisabled())
					atom[nAtom].initializeMovieQ(model.getMovie().getCapacity());
				atom[nAtom].setSelected(true);
				nAtom++;
				model.setNumberOfParticles(nAtom);
				break;
			case ADOB_ID:
				if (buffer instanceof RectangularObstacle) {
					RectangularObstacle r = (RectangularObstacle) buffer;
					if (!model.getRecorderDisabled())
						r.initializeMovieQ(model.getMovie().getCapacity());
					obstacles.add(r);
					r.setSelected(true);
				}
				break;
			case ADDI_ID:
				insertAMolecule(x, y, new DiatomicMolecule(DiatomicConfigure.typeOfA, DiatomicConfigure.typeOfB, DiatomicConfigure.distance, DiatomicConfigure.strength));
				break;
			case WATE_ID:
				insertAMolecule(x, y, new TriatomicMolecule(TriatomicConfigure.typeOfA, TriatomicConfigure.typeOfB, TriatomicConfigure.typeOfC, TriatomicConfigure.d12, TriatomicConfigure.s12, TriatomicConfigure.d23, TriatomicConfigure.s23, TriatomicConfigure.angle, TriatomicConfigure.strength));
				break;
			case BENZ_ID:
				insertAMolecule(x, y, new Benzene());
				break;
			case ADCH_ID:
				insertAMolecule(x, y, new ChainMolecule(ChainConfigure.typeOfAtom, ChainConfigure.number, ChainConfigure.growMode, ChainConfigure.distance, ChainConfigure.angle));
				break;
			case UndoAction.INSERT_A_MOLECULE:
				undoRemoveMarkedAtoms(nInserted);
				molecules.getMolecule(model.getAtom(model.getNumberOfParticles() - 1)).setSelected(true);
				break;
			case UndoAction.FILL_AREA_WITH_PARTICLES:
				if (area != null)
					fillAreaWithAtoms(area, latticeType, elementID);
				break;
			case DGBD_ID:
			case BBON_ID:
				if (buffer instanceof RadialBond) {
					bonds.add((RadialBond) buffer);
					((RadialBond) buffer).setSelected(true);
					MoleculeCollection.sort(model);
				}
				break;
			case BBEN_ID:
				if (buffer instanceof AngularBond) {
					bends.add((AngularBond) buffer);
					((AngularBond) buffer).setSelected(true);
				}
				break;
			case AACD_ID:
				undoRemoveMarkedAtoms(1);
				break;
			case ANTD_ID:
				undoRemoveMarkedAtoms(2);
				break;
			case SREC_ID:
			case SCUR_ID:
			case SCIR_ID:
				if (buffer instanceof Object[]) {
					Object[] o = (Object[]) buffer;
					molecules.add(new CurvedSurface((Polygon) o[1], model));
					molecules.get(molecules.size() - 1).setSelected(true);
				}
				break;
			case RREC_ID:
			case RCIR_ID:
				if (buffer instanceof Object[]) {
					Object[] o = (Object[]) buffer;
					molecules.add(new CurvedRibbon((Polygon) o[1], model, true));
					molecules.get(molecules.size() - 1).setSelected(true);
				}
				break;
			case RCUR_ID:
				if (buffer instanceof Object[]) {
					Object[] o = (Object[]) buffer;
					molecules.add(new CurvedRibbon((Polygon) o[1], model, ((Boolean) o[0]).booleanValue()));
					molecules.get(molecules.size() - 1).setSelected(true);
				}
				break;
			case SFRE_ID:
				if (buffer instanceof Object[]) {
					Object[] o = (Object[]) buffer;
					boolean closed = ((Boolean) o[0]).booleanValue();
					molecules.add(closed ? new CurvedSurface((Polygon) o[1], model) : new CurvedRibbon((Polygon) o[1], model, false));
					molecules.get(molecules.size() - 1).setSelected(true);
				}
				break;
			}
			doNotFireUndoEvent = false;
			model.notifyChange();
			repaint();
		}

	}

	private class UndoableDeletion extends AbstractUndoableEdit {

		private String presentationName;
		private int undoID, nRemoved;
		private Object removedObject;
		private RectangularObstacle[] removedObstacles;
		private Layered[] removedLayers;

		UndoableDeletion(int undoID) {
			this.undoID = undoID;
			switch (undoID) {
			case UndoAction.BLOCK_REMOVE:
				presentationName = "Deletion";
				break;
			case SACD_ID:
				presentationName = "Shortening Polypeptide";
				break;
			case SNTD_ID:
				presentationName = "Shottening DNA Strand";
				break;
			case UndoAction.REMOVE_RADIAL_BOND:
				presentationName = "Removing Radial Bond";
				break;
			case UndoAction.REMOVE_ANGULAR_BOND:
				presentationName = "Removing Angular Bond";
				break;
			case UndoAction.REMOVE_OBSTACLE:
				presentationName = "Removing Rectangular Obstacle";
				break;
			}
		}

		UndoableDeletion(int undoID, Object removedObject) {
			this(undoID);
			this.removedObject = removedObject;
		}

		UndoableDeletion(int undoID, int nRemoved) {
			this(undoID);
			this.nRemoved = nRemoved;
		}

		UndoableDeletion(int undoID, int nRemoved, RectangularObstacle[] removedObstacles) {
			this(undoID, nRemoved);
			this.removedObstacles = removedObstacles;
		}

		UndoableDeletion(int undoID, int nRemoved, RectangularObstacle[] removedObstacles, Layered[] removedLayers) {
			this(undoID, nRemoved, removedObstacles);
			this.removedLayers = removedLayers;
		}

		public String getPresentationName() {
			return presentationName;
		}

		public void undo() {
			super.undo();
			switch (undoID) {
			case UndoAction.BLOCK_REMOVE:
				undoRemoveMarkedAtoms(nRemoved);
				if (removedObstacles != null) {
					for (RectangularObstacle i : removedObstacles)
						obstacles.add(i);
					if (nRemoved <= 0 && removedObstacles.length == 1)
						removedObstacles[0].setSelected(true);
				}
				if (removedLayers != null) {
					for (Layered i : removedLayers)
						layerBasket.add(i);
					if (nRemoved <= 0 && removedLayers.length == 1)
						((ModelComponent) removedLayers[0]).setSelected(true);
				}
				break;
			case UndoAction.REMOVE_RADIAL_BOND:
				if (removedObject instanceof Object[]) {
					Object[] o = (Object[]) removedObject;
					if (o[0] instanceof RadialBond) {
						bonds.add((RadialBond) o[0]);
						((RadialBond) o[0]).setSelected(true);
					}
					if (o[1] instanceof List) {
						List x = (List) o[1];
						for (Iterator it = x.iterator(); it.hasNext();)
							bends.add((AngularBond) it.next());
					}
					MoleculeCollection.sort(model);
				}
				break;
			case UndoAction.REMOVE_ANGULAR_BOND:
				if (removedObject instanceof AngularBond) {
					bends.add((AngularBond) removedObject);
					((AngularBond) removedObject).setSelected(true);
				}
				break;
			case UndoAction.REMOVE_OBSTACLE:
				if (removedObject instanceof RectangularObstacle) {
					RectangularObstacle r = (RectangularObstacle) removedObject;
					if (!model.getRecorderDisabled())
						r.initializeMovieQ(model.getMovie().getCapacity());
					obstacles.add(r);
					r.setSelected(true);
				}
				break;
			case SACD_ID:
				undoRemoveMarkedAtoms(1);
				break;
			case SNTD_ID:
				undoRemoveMarkedAtoms(2);
				break;
			}
			model.notifyChange();
			repaint();
		}

		public void redo() {
			super.redo();
			doNotFireUndoEvent = true;
			switch (undoID) {
			case UndoAction.BLOCK_REMOVE:
				List<Integer> list = new ArrayList<Integer>();
				int n = model.getNumberOfParticles();
				for (int i = 0; i < nRemoved; i++)
					list.add(n - 1 - i);
				removeMarkedAtoms(list);
				if (removedObstacles != null)
					for (RectangularObstacle o : removedObstacles)
						obstacles.remove(o);
				if (removedLayers != null)
					for (Layered l : removedLayers)
						layerBasket.remove(l);
				break;
			case UndoAction.REMOVE_RADIAL_BOND:
				if (removedObject instanceof Object[]) {
					Object[] o = (Object[]) removedObject;
					if (o[0] instanceof RadialBond) {
						bonds.remove((RadialBond) o[0]);
						if (((RadialBond) o[0]).isSelected())
							setSelectedComponent(null);
					}
					if (o[1] instanceof List) {
						List x = (List) o[1];
						for (Iterator it = x.iterator(); it.hasNext();)
							bends.remove((AngularBond) it.next());
					}
					MoleculeCollection.sort(model);
				}
				break;
			case UndoAction.REMOVE_ANGULAR_BOND:
				if (removedObject instanceof AngularBond) {
					bends.remove((AngularBond) removedObject);
					if (((AngularBond) removedObject).isSelected())
						setSelectedComponent(null);
				}
				break;
			case UndoAction.REMOVE_OBSTACLE:
				if (removedObject instanceof RectangularObstacle) {
					RectangularObstacle r = (RectangularObstacle) removedObject;
					if (!model.getRecorderDisabled())
						r.initializeMovieQ(-1);
					obstacles.remove(r);
					if (r.isSelected())
						setSelectedComponent(null);
				}
				break;
			case SACD_ID:
				list = new ArrayList<Integer>();
				list.add(model.getNumberOfParticles() - 1);
				removeMarkedAtoms(list);
				break;
			case SNTD_ID:
				list = new ArrayList<Integer>();
				list.add(model.getNumberOfParticles() - 2);
				list.add(model.getNumberOfParticles() - 1);
				removeMarkedAtoms(list);
				break;
			}
			doNotFireUndoEvent = false;
			model.notifyChange();
			repaint();
		}
	}

	private class UndoableMoving extends AbstractUndoableEdit {

		private ModelComponent mc;
		private double x, y, angle;
		private String presentationName;
		private Point2D redoCOM;
		private Map<Atom, Point2D> redoCRD;

		private void storeRedoState(Rotatable r) {
			if (r instanceof Molecule) {
				Molecule mol = (Molecule) r;
				redoCOM = mol.getCenterOfMass2D();
				if (redoCRD == null) {
					redoCRD = new HashMap<Atom, Point2D>();
				} else {
					redoCRD.clear();
				}
				Atom at;
				synchronized (mol.getSynchronizedLock()) {
					for (Iterator it = mol.iterator(); it.hasNext();) {
						at = (Atom) it.next();
						redoCRD.put(at, new Point2D.Double(at.getRx(), at.getRy()));
					}
				}
			} else if (r instanceof ImageComponent) {

			}
		}

		private void redo(Rotatable r) {
			if (r instanceof Molecule) {
				if (redoCOM == null || redoCRD.isEmpty())
					return;
				Molecule mol = (Molecule) r;
				Atom at;
				Point2D point;
				synchronized (mol.getSynchronizedLock()) {
					for (Iterator it = mol.iterator(); it.hasNext();) {
						at = (Atom) it.next();
						point = redoCRD.get(at);
						if (point != null) {
							at.setRx(point.getX());
							at.setRy(point.getY());
						}
					}
				}
			} else if (r instanceof ImageComponent) {
				if (angle != 0)
					((ImageComponent) r).setAngle((float) angle);
			}
		}

		UndoableMoving(ModelComponent mc) {
			this.mc = mc;
			x = mc.getRx();
			y = mc.getRy();
			if (mc instanceof ImageComponent) {
				angle = ((ImageComponent) mc).getAngle();
			}
			switch (actionID) {
			case ROTA_ID:
				presentationName = "Rotation";
				if (mc instanceof Rotatable)
					storeRedoState((Rotatable) mc);
				break;
			case SELE_ID:
				presentationName = "Translation";
				break;
			}
		}

		public void setPresentationName(String s) {
			presentationName = s;
		}

		public String getPresentationName() {
			return presentationName;
		}

		public void undo() {
			super.undo();
			mc.restoreState();
			if (!mc.isSelected())
				mc.setSelected(true);
			model.notifyChange();
			if (useJmol)
				refreshJmol();
			repaint();
		}

		public void redo() {
			super.redo();
			doNotFireUndoEvent = true;
			if (presentationName.equals("Translation")) {
				if (mc instanceof Atom)
					((Atom) mc).translateTo(x, y);
				else if (mc instanceof Molecule)
					((Molecule) mc).translateTo(x, y);
				else if (mc instanceof RectangularObstacle)
					((RectangularObstacle) mc).translateCenterTo(x, y);
				else if (mc instanceof Layered) {
					((Layered) mc).setLocation(x, y);
					ModelComponent host = ((Layered) mc).getHost();
					if (host instanceof Atom) {
						Atom a = (Atom) host;
						a.translateTo(((Layered) mc).getCenter());
					}
				}
			} else if (presentationName.equals("Rotation")) {
				if (mc instanceof Rotatable)
					redo((Rotatable) mc);
			}
			doNotFireUndoEvent = false;
			if (!mc.isSelected())
				mc.setSelected(true);
			model.notifyChange();
			if (useJmol)
				refreshJmol();
			repaint();
		}

	}

	private class UndoableResizing extends AbstractUndoableEdit {

		private ModelComponent mc;
		private double x, y, w, h;
		private String presentationName = "";

		UndoableResizing(ModelComponent mc) {
			this.mc = mc;
			if (mc instanceof RectangularObstacle) {
				x = ((RectangularObstacle) mc).getX();
				y = ((RectangularObstacle) mc).getY();
				w = ((RectangularObstacle) mc).getWidth();
				h = ((RectangularObstacle) mc).getHeight();
				presentationName = "Resizing Rectangular Obstacle";
			} else if (mc instanceof Atom) {
				if (actionID == VELO_ID) {
					x = ((Atom) mc).getVx();
					y = ((Atom) mc).getVy();
					presentationName = "Changing Velocity";
				}
			}
		}

		public String getPresentationName() {
			return presentationName;
		}

		public void undo() {
			super.undo();
			mc.restoreState();
			if (!mc.isSelected())
				mc.setSelected(true);
			model.notifyChange();
			repaint();
		}

		public void redo() {
			super.redo();
			doNotFireUndoEvent = true;
			if (mc instanceof RectangularObstacle) {
				((RectangularObstacle) mc).setRect(x, y, w, h);
			} else if (mc instanceof Atom) {
				((Atom) mc).setVx(x);
				((Atom) mc).setVy(y);
			}
			doNotFireUndoEvent = false;
			if (!mc.isSelected())
				mc.setSelected(true);
			model.notifyChange();
			repaint();
		}

	}

	/** Serializable state. */
	public static class State extends MDView.State {

		private byte displayStyle = StyleConstant.SPACE_FILLING;
		private int probeID = Element.ID_NT;
		private int efCellSize = 10;
		private byte efShadingMode = ElectricForceField.TRANSPARENCY_SHADING_MODE;
		private boolean shading, chargeShading, showVVectors, showVDWCircles, showVDWLines, showChargeLines, showSSLines, showBPLines, showPVectors, showAVectors, showFVectors, showContour, showExcitation = true, showEFieldLines, useJmol;
		private double probeCharge;
		private int[] elementColors; // an array to store the colors of the four adjustable elements
		private Color[] moColors; // an array to store the colors of the molecular surface objects
		private float vdwLinesRatio = 2.0f, vdwLineThickness = 1;
		private byte vdwCircleStyle = StyleConstant.VDW_DOTTED_CIRCLE;

		public State() {
			super();
			elementColors = new int[Element.getNumberOfElements()];
			Arrays.fill(elementColors, 0xffffff);
			elementColors[Element.ID_PL] = 0x00ff00;
			elementColors[Element.ID_WS] = 0x0000ff;
			elementColors[Element.ID_CK] = 0xff00ff;
			elementColors[Element.ID_MO] = 0xffc800;
			elementColors[Element.ID_SP] = 0xffff00;
			elementColors[Element.ID_A] = 0x7da7d9;
			elementColors[Element.ID_C] = 0xc4df9a;
			elementColors[Element.ID_G] = 0xfdc588;
			elementColors[Element.ID_T] = 0xfff699;
			elementColors[Element.ID_U] = 0xfff699;
		}

		public void setUseJmol(boolean b) {
			useJmol = b;
		}

		public boolean getUseJmol() {
			return useJmol;
		}

		public void setDisplayStyle(byte i) {
			displayStyle = i;
		}

		public byte getDisplayStyle() {
			return displayStyle;
		}

		public void setMonochromatic(boolean b) {
			useJmol = !b;
		}

		public boolean getMonochromatic() {
			return !useJmol;
		}

		public void setShowVDWLines(boolean b) {
			showVDWLines = b;
		}

		public boolean getShowVDWLines() {
			return showVDWLines;
		}

		public void setVDWLinesRatio(float ratio) {
			vdwLinesRatio = ratio;
		}

		public float getVDWLinesRatio() {
			return vdwLinesRatio;
		}

		public void setVDWLineThickness(float thickness) {
			vdwLineThickness = thickness;
		}

		public float getVDWLineThickness() {
			return vdwLineThickness;
		}

		public void setShowVDWCircles(boolean b) {
			showVDWCircles = b;
		}

		public boolean getShowVDWCircles() {
			return showVDWCircles;
		}

		public void setVDWCircleStyle(byte i) {
			vdwCircleStyle = i;
		}

		public byte getVDWCircleStyle() {
			return vdwCircleStyle;
		}

		public void setShowChargeLines(boolean b) {
			showChargeLines = b;
		}

		public boolean getShowChargeLines() {
			return showChargeLines;
		}

		public void setShowSSLines(boolean b) {
			showSSLines = b;
		}

		public boolean getShowSSLines() {
			return showSSLines;
		}

		public void setShowBPLines(boolean b) {
			showBPLines = b;
		}

		public boolean getShowBPLines() {
			return showBPLines;
		}

		public void setShowEFieldLines(boolean b) {
			showEFieldLines = b;
		}

		public boolean getShowEFieldLines() {
			return showEFieldLines;
		}

		public void setShading(boolean b) {
			shading = b;
		}

		public boolean getShading() {
			return shading;
		}

		public void setChargeShading(boolean b) {
			chargeShading = b;
		}

		public boolean getChargeShading() {
			return chargeShading;
		}

		public void setShowExcitation(boolean b) {
			showExcitation = b;
		}

		public boolean getShowExcitation() {
			return showExcitation;
		}

		public void setShowVVectors(boolean b) {
			showVVectors = b;
		}

		public boolean getShowVVectors() {
			return showVVectors;
		}

		public void setShowPVectors(boolean b) {
			showPVectors = b;
		}

		public boolean getShowPVectors() {
			return showPVectors;
		}

		public void setShowAVectors(boolean b) {
			showAVectors = b;
		}

		public boolean getShowAVectors() {
			return showAVectors;
		}

		public void setShowFVectors(boolean b) {
			showFVectors = b;
		}

		public boolean getShowFVectors() {
			return showFVectors;
		}

		public void setElementColors(int[] i) {
			elementColors = i;
		}

		public int[] getElementColors() {
			return elementColors;
		}

		public void setShowContour(boolean b) {
			showContour = b;
		}

		public boolean getShowContour() {
			return showContour;
		}

		public void setProbeID(int id) {
			probeID = id;
		}

		public int getProbeID() {
			return probeID;
		}

		public void setProbeCharge(double c) {
			probeCharge = c;
		}

		public double getProbeCharge() {
			return probeCharge;
		}

		public void setEFCellSize(int i) {
			efCellSize = i;
		}

		public int getEFCellSize() {
			return efCellSize;
		}

		public void setEFShadingMode(byte shadingMode) {
			efShadingMode = shadingMode;
		}

		public byte getEFShadingMode() {
			return efShadingMode;
		}

		public void setMolecularObjectColors(Color[] c) {
			moColors = c;
		}

		public Color[] getMolecularObjectColors() {
			return moColors;
		}