org.eclipse.rwt.widgets.codemirror.CodeMirror.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.rwt.widgets.codemirror.CodeMirror.java

Source

/* 
 * polymap.org
 * Copyright 2011, Falko Brutigam, and other contributors as indicated
 * by the @authors tag.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */
package org.eclipse.rwt.widgets.codemirror;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.rwt.widgets.codemirror.internal.CallMethodCommand;
import org.eclipse.rwt.widgets.codemirror.internal.CodeMirrorJSService;
import org.eclipse.rwt.widgets.codemirror.internal.CodeMirrorLCA;
import org.eclipse.rwt.widgets.codemirror.internal.RenderCommand;
import org.eclipse.rwt.widgets.codemirror.internal.SetPropertyCommand;
import org.eclipse.rwt.widgets.codemirror.internal.CodeMirrorLCA.WidgetAdapter;

import org.eclipse.core.runtime.ListenerList;

/**
 * Widget that provides a <a href="http://codemirror.net">CodeMirror</a> syntax
 * highlighting code editor.
 * 
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 */
public class CodeMirror extends Control {

    private static Log log = LogFactory.getLog(CodeMirror.class);

    public final static String PROP_TEXT = "text";
    public final static String PROP_SELECTION = "selection";
    public final static String PROP_CURSOR_POS = "cursorpos";
    /** This is a pseudo property that indicated that 'Ctrl-S' was preset on the client. */
    public final static String PROP_SAVE = "save";

    /** External CodeMirror library location. **/
    private String js_location = CodeMirrorJSService.getBaseUrl();

    private String text = StringUtils.EMPTY;

    private int cursorPos;

    private LineMarkers lineMarkers = new LineMarkers();

    private TextSelection selection;

    private LCAAdapter lcaAdapter = new LCAAdapter();

    private ListenerList listeners = new ListenerList();

    static {
        CodeMirrorJSService.register();
    }

    public CodeMirror(Composite parent, int style) {
        super(parent, style);
    }

    public CodeMirror(Composite parent, int style, String js_location) {
        this(parent, style);
        this.js_location = js_location;
    }

    public Object getAdapter(Class adapter) {
        if (adapter.isAssignableFrom(CodeMirrorLCA.class)) {
            return new CodeMirrorLCA();
        } else if (adapter.isAssignableFrom(WidgetAdapter.class)) {
            return lcaAdapter;
        } else {
            return super.getAdapter(adapter);
        }
    }

    public String getJSLocation() {
        return js_location;
    }

    //    @Override
    //    public void setLayout( final Layout layout ) {
    //        throw new UnsupportedOperationException( "setLayout()" );
    //    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text != null ? text : StringUtils.EMPTY;
        lcaAdapter.pushCommand(new SetPropertyCommand(PROP_TEXT, text));
        firePropertyEvent(PROP_TEXT, this.text, null);
    }

    public int getCursorPos() {
        return cursorPos;
    }

    public LineMarkers lineMarkers() {
        return lineMarkers;
    }

    public void setSelection(int start, int end) {
        selection = new TextSelection(start, end);
        lcaAdapter.pushCommand(new CallMethodCommand("setSelection", start, end));
        firePropertyEvent(PROP_SELECTION, getSelection(), null);
    }

    public void clearSelection() {
        selection = null;
        lcaAdapter.pushCommand(new CallMethodCommand("setSelection", 0, 0));
        firePropertyEvent(PROP_SELECTION, getSelection(), null);
    }

    public TextSelection getSelection() {
        return selection;
    }

    /**
     * Add a listener to this editor. The following properties fire events:
     * <ul>
     * <li>{@link #PROP_TEXT}</li>
     * <li>{@link #PROP_SELECTION}</li>
     * <li>{@link #PROP_CURSOR_POS}</li>
     * </ul>
     * 
     * @param l
     */
    public void addPropertyChangeListener(PropertyChangeListener l) {
        listeners.add(l);
    }

    public void removePropertyCHangeListener(PropertyChangeListener l) {
        listeners.remove(l);
    }

    protected void firePropertyEvent(String prop, Object newValue, Object oldValue) {
        PropertyChangeEvent ev = new PropertyChangeEvent(this, prop, oldValue, newValue);
        for (Object l : listeners.getListeners()) {
            ((PropertyChangeListener) l).propertyChange(ev);
        }
    }

    public void openCompletions(List<ICompletion> proposals) {
        StringBuilder json = new StringBuilder(1024);
        json.append('[');
        for (ICompletion proposal : proposals) {
            json.append(json.length() > 1 ? "," : "");
            json.append("{\"text\":\"").append(proposal.getCompletion()).append("\",");
            json.append("\"replaceStart\":").append(proposal.getReplaceStart()).append(",");
            json.append("\"replaceEnd\":").append(proposal.getReplaceEnd()).append("}");
        }
        json.append(']');
        lcaAdapter.pushCommand(new CallMethodCommand("openCompletions", json.toString()));
    }

    /**
     * The internal interface for the {@link CodeMirrorLCA}.
     */
    class LCAAdapter implements WidgetAdapter {

        private boolean load_lib_done = false;

        private Queue<RenderCommand> commandQueue = new LinkedList();

        public void setText(String text) {
            String old = CodeMirror.this.text;
            CodeMirror.this.text = text;
            firePropertyEvent(PROP_TEXT, CodeMirror.this.text, old);
        }

        public void setCursorPos(int cursorPos) {
            int old = CodeMirror.this.cursorPos;
            CodeMirror.this.cursorPos = cursorPos;
            firePropertyEvent(PROP_CURSOR_POS, CodeMirror.this.cursorPos, old);
        }

        public void forceSave() {
            firePropertyEvent(PROP_SAVE, Boolean.TRUE, null);
        }

        public boolean isJSLoaded() {
            return load_lib_done;
        }

        public void setJSLoaded(boolean loaded) {
            load_lib_done = loaded;
        }

        public Iterable<RenderCommand> nextCommands() {
            Queue<RenderCommand> result = commandQueue;
            commandQueue = new LinkedList();
            return result;
        }

        void pushCommand(RenderCommand command) {
            commandQueue.offer(command);
        }
    };

    /**
     * 
     */
    public class LineMarkers extends TreeMap<Integer, ILineMarker> {

        public void clear() {
            super.clear();
            lcaAdapter.pushCommand(new CallMethodCommand("clearLineMarkers"));
        }

        public ILineMarker put(ILineMarker marker) {
            return put(marker.getLine(), marker);
        }

        public ILineMarker put(Integer line, ILineMarker marker) {
            ILineMarker result = super.put(marker.getLine(), marker);

            StringBuilder buf = new StringBuilder(256);
            buf.append(
                    "<div style=\"font-weight:bold;display:inline;position:relative;left:0px;top:0px;vertical-align:top;float:left;width:16px;");
            //            if (marker.getFgColor() != null) {
            //                buf.append( "color:" ).append( marker.getFgColor() ).append( ";" );
            //            }
            String textClassName = "cm-error";
            if (marker.getIcon() != null) {
                // XXX insert real image URL here
                String imageUrl = "icons/error_tsk.gif";
                if (marker.getIcon().resourceName.contains("warn")) {
                    imageUrl = "icons/warn_tsk.gif";
                    textClassName = "cm-warn";
                } else if (marker.getIcon().resourceName.contains("info")) {
                    imageUrl = "icons/info_tsk.gif";
                    textClassName = "cm-info";
                }
                buf.append("background:url(").append(js_location).append("&res=" + imageUrl + ");");
            }
            buf.append("\" ");
            if (marker.getText() != null) {
                buf.append("title=\"").append(marker.getText()).append("\"");
            }
            buf.append("> </div>%N%");

            lcaAdapter.pushCommand(new CallMethodCommand("setLineMarker", marker.getId(), marker.getLine(),
                    marker.getCharStart(), marker.getCharEnd(), textClassName, buf.toString()));
            return result;
        }

        public void putAll(Map<? extends Integer, ? extends ILineMarker> map) {
            throw new RuntimeException("not yet implemented");
        }
    }

    /**
     * 
     */
    public class TextSelection {

        private int start, end;

        protected TextSelection(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public int getStart() {
            return start;
        }

        public int getEnd() {
            return end;
        }

        public String getText() {
            return StringUtils.substring(text, start, end);
        }
    }

}