de.tud.cs.st.vespucci.vespucci_model.diagram.sheet.ChangedAbstractBasicTextPropertySection.java Source code

Java tutorial

Introduction

Here is the source code for de.tud.cs.st.vespucci.vespucci_model.diagram.sheet.ChangedAbstractBasicTextPropertySection.java

Source

/******************************************************************************
 * Copyright (c) 2005, 2006 IBM Corporation 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: IBM Corporation - initial API and implementation
 ****************************************************************************/
package de.tud.cs.st.vespucci.vespucci_model.diagram.sheet;

import java.util.ArrayList;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.core.util.StringStatics;
import org.eclipse.gmf.runtime.common.ui.util.StatusLineUtil;
import org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection;
import org.eclipse.gmf.runtime.diagram.ui.properties.views.TextChangeHelper;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.osgi.framework.FrameworkUtil;

import de.tud.cs.st.vespucci.io.KeywordReader;

/**
 * <p>A Changed Copy of AbstractBasicTextPropertySection
 * (org.eclipse.gmf.runtime.diagram
 * .ui.properties.sections.AbstractBasicTextPropertySection)</p>
 * 
 * <p><i>Reviewd by Thomas Schulz, Alexander Weitzmann (Aug. 16, 2011)</i><br/>
 * <i>Reviewed by Dominic Scheurer (Sept. 18, 2011)</i></p>
 *
 * Original version by:
 * 
 * @author Natalia Balaba
 * 
 *         Changed by:
 * @author Malte Viering
 * @author Benjamin Lck
 * @author Dominic Scheurer
 * @author Alexander Weitzmann
 * @author Thomas Schulz
 */
public abstract class ChangedAbstractBasicTextPropertySection extends AbstractModelerPropertySection {

    private static final ResourceBundle PLUGIN_RES_BUNDLE = ResourceBundle.getBundle("plugin");

    private static final int QUERY_TAB_HEIGHT_SHIFT = 25;

    private static final int QUERY_TAB_WIDTH_SHIFT = 32;

    private static final int START_HEIGHT = 15;

    /** styled text widget to display and set value of the property */
    private MarkableStyledText styledTextWidget;

    /** parent parent ... parent composite for the size of the text field */
    private Composite scrolledParent;

    private Composite sectionComposite;

    /**
     * Preference-store of the java source viewer. Used for highlighting and
     * text-settings for query.
     */
    private static final IPreferenceStore SRC_VIEWER_PREFS = PreferenceConstants.getPreferenceStore();

    Listener resizeLinstener = new Listener() {
        @Override
        public void handleEvent(final Event e) {
            updateHeight();
            scrolledParent.getVerticalBar().setVisible(false);
            scrolledParent.getHorizontalBar().setVisible(false);
        }
    };

    /**
     * A helper to listen for events that indicate that a text field has been
     * changed.
     */
    private final TextChangeHelper textChangeListener = new TextChangeHelper() {

        private boolean textModified = false;

        /**
         * Keywords to be marked
         */
        private final String[] keywords = KeywordReader.readAndParseResourceFile(
                FrameworkUtil.getBundle(getClass()).getSymbolicName(),
                PLUGIN_RES_BUNDLE.getString("queryKeywordsFile"));

        /**
         * Pattern to be used to match strings in query including the single quotes
         */
        private static final String STRING_PATTERN = "'.+?'";

        /**
         * Performs text highlighting of the query properties tab.
         * 
         * @author Dominic Scheurer
         * @author Alexander Weitzmann
         */
        private void doSyntaxHighlighting() {
            startNonUserChange();

            // first, set everything to black and normal
            resetStyle();

            // highlight bracket, depending on caret
            highlightBrackets();

            // highlight keywords
            highlightKeywords();

            // highlight strings
            highlightStrings();

            finishNonUserChange();
        }

        /**
         * Returns a StyleRange that surrounds the character at the given
         * position with a rectangle.
         * 
         * @param position
         *            The position of the bracket to be highlighted.
         * @return A StyleRange that highlights bracket at given position
         */
        private StyleRange getBracketStyle(final int position) {
            final StyleRange bracketStyle = styledTextWidget.getStyleRangeAtOffset(position);

            // surround with rectangle
            bracketStyle.borderStyle = SWT.BORDER_SOLID;

            return bracketStyle;
        }

        /**
         * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
         */
        @Override
        public void handleEvent(final Event event) {
            switch (event.type) {
            case SWT.KeyDown:
                doSyntaxHighlighting();

                textModified = true;
                if (event.character == SWT.CR) {
                    getPropertyValueString();
                }
                break;
            case SWT.FocusOut:
                textChanged((Control) event.widget);
                break;
            case SWT.FocusIn:
                doSyntaxHighlighting();
                break;
            case SWT.MouseDown:
                doSyntaxHighlighting();
                break;
            default:
                break;
            }
        }

        /**
         * Highlights the corresponding bracket relative to caret position
         */
        private void highlightBrackets() {

            final int offset = styledTextWidget.getCaretOffset();
            // Check if caret is at first position
            // => no bracket highlighting
            if (offset == 0) {
                return;
            }

            final int size = styledTextWidget.getCharCount();

            final char currentChar = styledTextWidget.getText().charAt(offset - 1);

            // mark closing or opening bracket
            markCorrespondingBrackets(offset, size, currentChar);

        }

        /*
         * Brackets in strings will be ignored (purpose of: textWidget.isPositionMarked(i))
         */
        private void markCorrespondingBrackets(final int offset, final int size, final char currentChar) {
            if (currentChar == '(' && !styledTextWidget.isPositionMarked(offset - 1)) {
                markCorrespondingClosingBracket(offset, size);
            } else if (currentChar == ')' && offset > 1 && !styledTextWidget.isPositionMarked(offset - 1)) {
                markCorrespondingOpeningBracket(offset);
            }
        }

        private void markCorrespondingClosingBracket(final int offset, final int size) {
            int furtherOpeningBrackets = 0;
            for (int i = offset; i < size; i++) {
                if (styledTextWidget.getText().charAt(i) == '(' && !styledTextWidget.isPositionMarked(i)) {
                    furtherOpeningBrackets++;
                }

                if (styledTextWidget.getText().charAt(i) == ')' && furtherOpeningBrackets == 0
                        && !styledTextWidget.isPositionMarked(i)) {
                    // highlight corresponding bracket
                    styledTextWidget.setStyleRange(getBracketStyle(i));
                    return;
                }

                if (styledTextWidget.getText().charAt(i) == ')' && !styledTextWidget.isPositionMarked(i)) {
                    furtherOpeningBrackets--;
                }
            }
        }

        private void markCorrespondingOpeningBracket(final int offset) {
            int furtherClosingBrackets = 0;
            for (int i = offset - 2; i >= 0; i--) {
                if (styledTextWidget.getText().charAt(i) == ')' && !styledTextWidget.isPositionMarked(i)) {
                    furtherClosingBrackets++;
                }

                if (styledTextWidget.getText().charAt(i) == '(' && furtherClosingBrackets == 0
                        && !styledTextWidget.isPositionMarked(i)) {
                    // highlight corresponding bracket
                    styledTextWidget.setStyleRange(getBracketStyle(i));
                    return;
                }

                if (styledTextWidget.getText().charAt(i) == '(' && !styledTextWidget.isPositionMarked(i)) {
                    furtherClosingBrackets--;
                }
            }
        }

        /**
         * Highlights all keywords and strings.
         */
        private void highlightKeywords() {
            final String targetText = styledTextWidget.getText();
            Pattern keywordPattern = null;
            Matcher keywordMatcher = null;

            final StyleRange styleRange = prepareKeywordsStyleRange();

            // highlight keywords
            for (final String str : keywords) {
                keywordPattern = Pattern.compile(String.format("\\b%s\\b", str));
                keywordMatcher = keywordPattern.matcher(targetText);

                while (keywordMatcher.find()) {
                    styleRange.start = keywordMatcher.start();
                    styleRange.length = keywordMatcher.end() - keywordMatcher.start();

                    styledTextWidget.setStyleRange(styleRange);
                }
            }
        }

        private StyleRange prepareKeywordsStyleRange() {
            final boolean bold = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_BOLD);
            final boolean italic = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_ITALIC);

            final StyleRange styleRange = new StyleRange();
            styleRange.fontStyle = SWT.NORMAL;
            if (bold) {
                styleRange.fontStyle |= SWT.BOLD;
            }
            if (italic) {
                styleRange.fontStyle |= SWT.ITALIC;
            }
            styleRange.strikeout = SRC_VIEWER_PREFS
                    .getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_STRIKETHROUGH);
            styleRange.underline = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_UNDERLINE);
            styleRange.foreground = JavaUI.getColorManager()
                    .getColor(PreferenceConstants.EDITOR_JAVA_KEYWORD_COLOR);
            return styleRange;
        }

        /**
         * Highlights all strings
         */
        private void highlightStrings() {
            final Matcher stringMatcher = Pattern.compile(STRING_PATTERN, Pattern.DOTALL)
                    .matcher(styledTextWidget.getText());

            final StyleRange styleRange = prepareStringsStyleRange();

            // remove all string-markings for {@link #highlightBrackets}
            styledTextWidget.unmarkAllPositions();

            // highlight strings
            while (stringMatcher.find()) {
                styleRange.start = stringMatcher.start() + 1;
                styleRange.length = stringMatcher.end() - styleRange.start - 1;

                styledTextWidget.setStyleRange(styleRange);

                // mark string-locations for {@link #highlightBrackets}
                for (int i = stringMatcher.start(); i < stringMatcher.end(); ++i) {
                    styledTextWidget.markPosition(i);
                }
            }
        }

        private StyleRange prepareStringsStyleRange() {
            final boolean bold = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_BOLD);
            final boolean italic = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_ITALIC);

            final StyleRange styleRange = new StyleRange();
            styleRange.fontStyle = SWT.NORMAL;
            if (bold) {
                styleRange.fontStyle |= SWT.BOLD;
            }
            if (italic) {
                styleRange.fontStyle |= SWT.ITALIC;
            }
            styleRange.strikeout = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_STRIKETHROUGH);
            styleRange.underline = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_UNDERLINE);
            styleRange.foreground = JavaUI.getColorManager().getColor(PreferenceConstants.EDITOR_STRING_COLOR);
            return styleRange;
        }

        /**
         * Resets the whole text style in the StyledText component to its "normal" state.
         */
        private void resetStyle() {
            final Color foreground = JavaUI.getColorManager()
                    .getColor(PreferenceConstants.EDITOR_JAVA_DEFAULT_COLOR);
            final Color background = EditorsUI.getSharedTextColors().getColor(PreferenceConverter
                    .getColor(EditorsUI.getPreferenceStore(), AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND));

            final boolean bold = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_DEFAULT_BOLD);
            final boolean italic = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_DEFAULT_ITALIC);

            int fontstyle = SWT.NORMAL;
            if (bold) {
                fontstyle |= SWT.BOLD;
            }
            if (italic) {
                fontstyle |= SWT.ITALIC;
            }

            final StyleRange normalStyle = new StyleRange(0, styledTextWidget.getCharCount(), foreground,
                    background, fontstyle);
            styledTextWidget.setStyleRange(normalStyle);
        }

        @Override
        public void startListeningTo(final Control control) {
            control.addListener(SWT.FocusOut, this);
            control.addListener(SWT.Modify, this);
            control.addListener(SWT.FocusIn, this);
            control.addListener(SWT.MouseDown, this);
        }

        @Override
        public void textChanged(final Control control) {
            if (textModified) {
                // clear error message
                final IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
                        .getActivePart();
                StatusLineUtil.outputErrorMessage(part, StringStatics.BLANK);

                setPropertyValue(control);
                textModified = false;
            }
        }
    };

    /**
     * @return - the default implementation returns contents of the text widget
     *         as a new value for the property. Subclasses can could be
     *         override.
     */
    protected final Object computeNewPropertyValue() {
        return getTextWidget().getText();
    }

    /*
     * (non-Javadoc)
     * @see
     * org.eclipse.ui.views.properties.tabbed.ISection#createControls(org.eclipse
     * .swt.widgets.Composite,
     * org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage)
     */
    @Override
    public final void createControls(final Composite parent,
            final TabbedPropertySheetPage aTabbedPropertySheetPage) {
        doCreateControls(parent, aTabbedPropertySheetPage);
    }

    /**
     * Instantiate a text widget
     * 
     * @param parent
     *            - parent composite
     * @return - a text widget to display and edit the property
     */
    protected final MarkableStyledText createTextWidget(final Composite parent) {
        getSectionComposite().getSize();

        final MarkableStyledText st = new MarkableStyledText(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL);

        final Font userFont = JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT);
        st.setFont(userFont);

        final FormData data = new FormData();
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        data.top = new FormAttachment(0, 0);
        data.height = START_HEIGHT;
        st.setLayoutData(data);

        st.setBackground(EditorsUI.getSharedTextColors().getColor(PreferenceConverter
                .getColor(EditorsUI.getPreferenceStore(), AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND)));

        if (isReadOnly()) {
            st.setEditable(false);
        }

        return st;
    }

    /**
     * @see org.eclipse.ui.views.properties.tabbed.ISection#dispose()
     */
    @Override
    public final void dispose() {
        stopTextWidgetEventListener();
        super.dispose();
    }

    /**
     * Creates the GUI <code>Control</code> for this text property section
     * 
     * @param parent
     *            parent <code>Composite</code>
     * @param aTabbedPropertySheetPage
     *            <code>TabbedPropertySheetPage</code>
     * @see org.eclipse.gmf.runtime.common.ui.properties.ISection#createControls(org.eclipse.swt.widgets.Composite,
     *      org.eclipse.gmf.runtime.common.ui.properties.TabbedPropertySheetPage)
     */
    public final void doCreateControls(final Composite parent,
            final TabbedPropertySheetPage aTabbedPropertySheetPage) {
        super.createControls(parent, aTabbedPropertySheetPage);
        sectionComposite = getWidgetFactory().createFlatFormComposite(parent);
        styledTextWidget = createTextWidget(sectionComposite);

        scrolledParent = parent;

        while (true) {
            // TODO: There must be a nicer way for redraw the text widget then
            // to say the first scrolledcomposite .layout()
            // the scrolledParent should be "main" composite for the query tab
            if (scrolledParent instanceof ScrolledComposite) {
                break;
            }
            if (scrolledParent.getParent() == null) {
                break;
            }
            scrolledParent = scrolledParent.getParent();

        }

        scrolledParent.addListener(SWT.Resize, resizeLinstener);
        updateHeight();
        startTextWidgetEventListener();
    }

    private int getHeight() {
        return scrolledParent.getSize().y - QUERY_TAB_HEIGHT_SHIFT;
    }

    /**
     * @return Returns the listener.
     */
    protected final TextChangeHelper getListener() {
        return textChangeListener;
    }

    /**
     * @return - title of the command which will be executed to set the property
     */
    protected abstract String getPropertyChangeCommandName();

    /**
     * @return - name of the property to place in the label widget
     */
    protected abstract String getPropertyNameLabel();

    /**
     * returns as an array the property name
     * 
     * @return - array of strings where each describes a property name one per
     *         property. The strings will be used to calculate common indent
     *         from the left
     */
    protected final String[] getPropertyNameStringsArray() {
        return new String[] { getPropertyNameLabel() };
    }

    /**
     * @return - string representation of the property value
     */
    protected abstract String getPropertyValueString();

    /**
     * @return Returns the sectionComposite.
     */
    public final Composite getSectionComposite() {
        return sectionComposite;
    }

    /**
     * @return Returns the textWidget.
     */
    protected final StyledText getTextWidget() {
        return styledTextWidget;
    }

    private int getWidth() {
        return scrolledParent.getSize().x - QUERY_TAB_WIDTH_SHIFT;
    }

    /**
     * @see org.eclipse.ui.views.properties.tabbed.ISection#refresh()
     */
    @Override
    public final void refresh() {
        getListener().startNonUserChange();
        try {
            executeAsReadAction(new Runnable() {

                @Override
                public void run() {
                    refreshUI();
                }
            });
        } finally {
            getListener().finishNonUserChange();
        }
    }

    /**
     * Refresh UI body - refresh will surround this with read action block
     */
    protected final void refreshUI() {
        if (styledTextWidget != null) {
            styledTextWidget.setText(getPropertyValueString());
        }
    }

    /**
     * User pressed Enter key after editing text field - update the model
     * 
     * @param control
     *            <code>Control</code>
     */
    protected final synchronized void setPropertyValue(final Control control) {
        final Object value = computeNewPropertyValue();
        final ArrayList<ICommand> commands = new ArrayList<ICommand>();
        for (Object o : getEObjectList()) {
            final EObject next = (EObject) o;
            commands.add(createCommand(getPropertyChangeCommandName(), next, new Runnable() {

                @Override
                public void run() {
                    setPropertyValue(next, value);
                }

            }));
        }
        executeAsCompositeCommand(getPropertyChangeCommandName(), commands);
        refresh();
    }

    /**
     * Set property value for the given object
     * 
     * @param object
     *            - owner of the property
     * @param value
     *            - new value
     */
    protected abstract void setPropertyValue(EObject object, Object value);

    public final void setScrolledParent(final Composite scrolledParent) {
        this.scrolledParent = scrolledParent;
    }

    /**
     * Start listening to the text widget events
     */
    protected final void startTextWidgetEventListener() {
        if (!isReadOnly()) {
            getListener().startListeningTo(getTextWidget());
            getListener().startListeningForEnter(getTextWidget());
        }
    }

    /**
     * Stop listening to text widget events
     */
    protected final void stopTextWidgetEventListener() {
        if (!isReadOnly()) {
            getListener().stopListeningTo(getTextWidget());
        }
    }

    /**
     * calculates the new size of the widget and updates it
     */
    private void updateHeight() {
        if (getTextWidget() != null && !getTextWidget().isDisposed()) {
            getTextWidget().setVisible(false);
            scrolledParent.setVisible(false);
            final FormData data = new FormData();
            data.left = new FormAttachment(0, 0);
            data.right = new FormAttachment(100, 0);
            data.top = new FormAttachment(0, 0);
            data.height = getHeight();
            data.width = getWidth();
            getTextWidget().setLayoutData(data);
            final int HEIGHTS_SCROLLLINE = 30;
            Composite com = getSectionComposite();
            // TODO: there must be a nice way to update the heights and widths
            // of the textwidget and its parents
            while (true) {

                if (com instanceof ScrolledComposite) {
                    break;
                }
                if (com.getParent() == null) {
                    break;
                }
                com.setSize(getWidth(), getHeight() + HEIGHTS_SCROLLLINE);
                com = com.getParent();

            }
            com.layout();
            getTextWidget().setVisible(true);
            scrolledParent.setVisible(true);
        }
    }
}