/******************************************************************************* * Copyright (c) 2006 Sybase, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sybase, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.jst.pagedesigner.dnd.internal; import org.eclipse.gef.SharedCursors; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jst.pagedesigner.dom.EditModelQuery; import org.eclipse.jst.pagedesigner.dom.EditValidateUtil; import org.eclipse.jst.pagedesigner.dom.IDOMPosition; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Caret; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; import org.eclipse.wst.sse.ui.StructuredTextEditor; import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * This class will 1. determine it's insertion or update 2. call validator * corresponding helper to resolve it. * * @author mengbo */ public final class SourceViewerDragDropHelper { private static SourceViewerDragDropHelper _instance; /** * @return the singleton instance */ public static SourceViewerDragDropHelper getInstance() { if (_instance == null) { _instance = new SourceViewerDragDropHelper(); } return _instance; } private SourceViewerDragDropHelper() { // singleton, no external instantiation } private Point toControl(TextViewer textViewer, Point point) { return (textViewer != null ? textViewer.getTextWidget() .toControl(point) : point); } private int getDropOffset(StructuredTextEditor ste, Point pt) { StyledText st = ste.getTextViewer().getTextWidget(); int offset = st.getCaretOffset(); try { offset = st.getOffsetAtLocation(pt); } catch (IllegalArgumentException e) { boolean found = false; Point p = new Point((pt.x > 0 ? pt.x : 0), pt.y); // search nearest character for (; p.x > -1; p.x--) { try { offset = st.getOffsetAtLocation(p); /* * Now that a valid offset has been found, try to place at * the end of the line */ if (ste.getTextViewer() != null && ste.getTextViewer().getDocument() != null) { IRegion lineInfo = null; try { lineInfo = ste.getTextViewer().getDocument() .getLineInformationOfOffset(offset); } catch (BadLocationException e1) { // ignore exception and fall-through with lineInfo == null } if (lineInfo != null) offset = lineInfo.getOffset() + lineInfo.getLength(); } found = true; break; } catch (IllegalArgumentException ex) { // for trying location, no need to catch. } } if (!found) { offset = st.getCharCount(); } } return offset; } /** * @param textEditor * @param location * @param caret */ public void updateCaret(StructuredTextEditor textEditor, Point location, Point caret) { TextViewer textViewer = textEditor.getTextViewer(); if (textViewer != null) { Point pt = toControl(textViewer, location); StyledText st = textViewer.getTextWidget(); // auto scroll Rectangle ca = st.getClientArea(); int margin = st.getLineHeight(); if (pt.y < margin) { // up st.invokeAction(ST.LINE_UP); } else if (pt.y > ca.height - margin) { // down st.invokeAction(ST.LINE_DOWN); } // draw insertion point int offset = getDropOffset(textEditor, pt); if (offset != st.getCaretOffset()) { st.setCaretOffset(offset); st.setSelection(offset); } Point newCaret = st.getLocationAtOffset(offset); if (newCaret.equals(caret)) { return; } Caret ct = st.getCaret(); Point size = ct.getSize(); GC gc = new GC(st); //gc.setXORMode(true); gc.setLineWidth(size.x); // erase old caret if (caret != null) { Color originalForeground = gc.getForeground(); gc.setForeground(st.getBackground()); gc.drawLine(caret.x, caret.y, caret.x, caret.y + size.y); gc.setForeground(originalForeground); } st.redraw(); st.update(); // draw new caret if (caret == null) { caret = newCaret; } else { caret.x = newCaret.x; caret.y = newCaret.y; } if (ct.getImage() != null) { gc.drawImage(ct.getImage(), caret.x, caret.y); } else { gc.drawLine(caret.x, caret.y, caret.x, caret.y + size.y); } gc.dispose(); } } /** * @param textEditor * @param location */ public void updateCaret(StructuredTextEditor textEditor, Point location) { TextViewer textViewer = textEditor.getTextViewer(); if (textViewer != null) { Point pt = toControl(textViewer, location); StyledText st = textViewer.getTextWidget(); // auto scroll Rectangle ca = st.getClientArea(); int margin = st.getLineHeight(); if (pt.y < margin) { // up st.invokeAction(ST.LINE_UP); } else if (pt.y > ca.height - margin) { // down st.invokeAction(ST.LINE_DOWN); } // draw insertion point int offset = getDropOffset(textEditor, pt); if (offset != st.getCaretOffset()) { st.setCaretOffset(offset); st.setSelection(offset); } } } /** * @param textEditor * @param location * @return the caret offset */ public int showCaret(StructuredTextEditor textEditor, int location) { StyledText text = textEditor.getTextViewer().getTextWidget(); text.setCursor(SharedCursors.CURSOR_TREE_ADD); text.setCaretOffset(location); if (!text.isFocusControl()) { text.setFocus(); } return text.getCaretOffset(); } /** * @param node * @return the model query for the node or null if not available */ protected ModelQuery getModelQuery(Node node) { if (node.getNodeType() == Node.DOCUMENT_NODE) { return ModelQueryUtil.getModelQuery((Document) node); } return ModelQueryUtil.getModelQuery(node.getOwnerDocument()); } /** * @param caretPos * @param element * @return the position */ public IDOMPosition findPosition(int caretPos, Node element) { EditValidateUtil.validNode(element); IDOMPosition position = EditModelQuery.getInstance().createDomposition( ((IDOMNode) element).getModel(), caretPos, false); return position; } /** * @param viewer * @param node */ public void format(TextViewer viewer, Node node) { if (node == null) { return; } Node tmp; int start, offset; if (node.getPreviousSibling() != null) { tmp = node.getPreviousSibling(); start = ((IndexedRegion) tmp).getEndOffset(); } else { tmp = node; start = ((IndexedRegion) tmp).getStartOffset(); } if (node.getNextSibling() != null) { tmp = node.getNextSibling(); offset = ((IndexedRegion) tmp).getStartOffset() - start; } else { tmp = node; offset = ((IndexedRegion) tmp).getEndOffset() - start; } viewer.setSelectedRange(start, offset); viewer.doOperation(ISourceViewer.FORMAT); } /** * @param textEditor * @param reset */ public void changeCaret(StructuredTextEditor textEditor, boolean reset) { if (reset) { StyledText text = textEditor.getTextViewer().getTextWidget(); text.setCursor(new Cursor(null, SWT.CURSOR_IBEAM)); } } /** * @param textEditor * @param locationOffset * @return the location offset */ /*package*/ int getValidLocation(StructuredTextEditor textEditor, int locationOffset) { Node node = getCaretNode(textEditor, locationOffset); if (node == null) { // empty page? return 0; } if (node.getNodeType() == Node.TEXT_NODE) { return locationOffset; } return calculateCaretLocation(node, locationOffset); } /** * @param textEditor * @param location * @return the offset */ public int getOffset(StructuredTextEditor textEditor, Point location) { StyledText text = textEditor.getTextViewer().getTextWidget(); return text.getOffsetAtLocation(location); } // private IStructuredModel getModel(StructuredTextEditor textEditor) // { // IStructuredModel model = null; // if (textEditor.getDocumentProvider() != null) // { // if (textEditor.getDocumentProvider() instanceof IModelProvider) // { // model = ((IModelProvider) // textEditor.getDocumentProvider()).getModel(textEditor.getEditorInput()); // } // else // { // IDocument doc = // textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()); // if (doc instanceof IDocument) // { // model = // StructuredModelManager.getModelManager().getExistingModelForEdit(doc); // if (model == null) // { // model = // StructuredModelManager.getModelManager().getExistingModelForEdit((IDocument) // doc); // } // } // } // } // return model; // } /** * @param textEditor * @param pos * @return the node */ private Node getCaretNode(StructuredTextEditor textEditor, int pos) { // TODO: getModel is deprecated IStructuredModel model = textEditor.getModel(); // getModel(textEditor); if (model == null) { return null; } IndexedRegion inode = model.getIndexedRegion(pos); if (inode == null) { inode = model.getIndexedRegion(pos - 1); } return (inode instanceof Node) ? (Node) inode : null; } /** * Calculate and adjust the location in compare with Node. * * @param node * @param location * @return the location */ public int calculateCaretLocation(Node node, int location) { int pos[][] = new int[2][2]; pos[0][0] = EditModelQuery.getNodeStartIndex(node); pos[0][1] = EditModelQuery.getNodeStartNameEndIndex(node); pos[1][0] = EditModelQuery.getNodeEndNameStartIndex(node); pos[1][1] = EditModelQuery.getNodeEndIndex(node); if (pos[0][0] >= location || pos[1][0] == location || pos[1][1] <= location) { return location; } else if (pos[0][0] <= location && pos[0][1] >= location) { if (((pos[0][1] + pos[0][0]) / 2) >= location) { return pos[0][0]; } return pos[0][1]; } else if (pos[1][0] <= location && pos[1][1] >= location) { if (((pos[1][1] + pos[1][0]) / 2) >= location) { return pos[1][0]; } return pos[1][1]; } return location; } }