org.polymap.rhei.batik.toolkit.DefaultToolkit.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.rhei.batik.toolkit.DefaultToolkit.java

Source

/*
 * polymap.org
 * Copyright 2013, Falko Brutigam. All rights reserved.
 *
 * 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 3.0 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.polymap.rhei.batik.toolkit;

import static org.apache.commons.lang3.ArrayUtils.contains;

import java.util.ArrayList;
import java.util.concurrent.Callable;

import org.pegdown.FastEncoder;
import org.pegdown.LinkRenderer;
import org.pegdown.LinkRenderer.Rendering;
import org.pegdown.PegDownProcessor;
import org.pegdown.ast.AutoLinkNode;
import org.pegdown.ast.ExpImageNode;
import org.pegdown.ast.ExpLinkNode;
import org.pegdown.ast.RefLinkNode;
import org.pegdown.ast.WikiLinkNode;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Joiner;
import com.google.common.base.Throwables;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.widgets.MarkupValidator;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;

import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.Section;

import org.eclipse.rap.rwt.RWT;

import org.polymap.core.runtime.Lazy;
import org.polymap.core.runtime.LockedLazyInit;
import org.polymap.core.runtime.Polymap;
import org.polymap.core.ui.UIUtils;

import org.polymap.rhei.batik.PanelPath;
import org.polymap.rhei.batik.engine.PageStack;

/**
 *
 *
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 */
@SuppressWarnings("restriction")
public class DefaultToolkit implements IPanelToolkit {

    public static final String CSS_PREFIX = "batik-panel";
    public static final String CSS_SECTION = CSS_PREFIX + "-section";
    public static final String CSS_SECTION_EXPANDABLE = CSS_SECTION + "-expandable";
    public static final String CSS_SECTION_TITLE = CSS_PREFIX + "-section-title";
    public static final String CSS_SECTION_TITLE_EXPANDABLE = CSS_PREFIX + "-section-title-expandable";
    public static final String CSS_SECTION_SEPARATOR = CSS_PREFIX + "-section-separator";
    public static final String CSS_SECTION_CLIENT = CSS_PREFIX + "-section-client";

    public final Lazy<Color> COLOR_SECTION_TITLE_FG = new LockedLazyInit(() -> new Color(null, 0x4c, 0x85, 0xbc));
    public final Lazy<Color> COLOR_SECTION_TITLE_BG = new LockedLazyInit(() -> new Color(null, 0xbc, 0xe1, 0xf4));
    public final Lazy<Color> COLOR_SECTION_TITLE_BORDER = new LockedLazyInit(
            () -> new Color(null, 0x80, 0x80, 0xa0));

    private static ArrayList<Callable<IMarkdownRenderer>> mdRendererFactories = new ArrayList();

    /**
     * Static init: register standard Markdown renderer
     */
    static {
        registerMarkdownRenderer(new Callable<IMarkdownRenderer>() {
            @Override
            public PageLinkRenderer call() throws Exception {
                return new PageLinkRenderer();
            }
        });
    }

    public static void registerMarkdownRenderer(Callable<IMarkdownRenderer> factory) {
        mdRendererFactories.add(factory);
    }

    /**
     * The toolkit of the given control.
     * <p/>
     * XXX Current implementation return a toolkit with <b>no</b> connection to
     * the given control or its panel. Panel related methods do not work properly.
     */
    public static final DefaultToolkit of(Control control) {
        return new DefaultToolkit(null, null);
    }

    // instance *******************************************

    protected PanelPath panelPath;

    protected PageStack<PanelPath>.Page panelPage;

    protected FormColors colors;

    protected boolean closed;

    public DefaultToolkit(PanelPath panelPath, PageStack<PanelPath>.Page panelPage) {
        this.panelPath = panelPath;
        this.panelPage = panelPage;
    }

    public PanelPath getPanelPath() {
        return panelPath;
    }

    @Override
    public void close() {
        if (!isClosed()) {
            if (COLOR_SECTION_TITLE_BG.isInitialized()) {
                COLOR_SECTION_TITLE_BG.get().dispose();
            }
            if (COLOR_SECTION_TITLE_FG.isInitialized()) {
                COLOR_SECTION_TITLE_FG.get().dispose();
            }
            if (COLOR_SECTION_TITLE_BORDER.isInitialized()) {
                COLOR_SECTION_TITLE_BORDER.get().dispose();
            }
        }
        closed = true;
    }

    @Override
    public boolean isClosed() {
        return closed || panelPage != null && panelPage.control != null && panelPage.control.isDisposed();
    }

    @Override
    public Label createLabel(Composite parent, String text, int... styles) {
        Label result = adapt(new Label(parent, stylebits(styles)), false, false);
        if (text != null) {
            result.setText(text);
        }
        return result;
    }

    @Override
    public Label createFlowText(Composite parent, String text, int... styles) {
        Label result = new Label(parent, stylebits(styles) | SWT.WRAP) {
            //            private String callbackId;
            @Override
            public void setText(String _text) {
                String html = markdownToHtml(_text, this);
                super.setText(html);

                // LinkActionServiceHandler cannot trigger UIThread events, so this ensures that
                // there is a UI callback active; However, this leads to activate UICallbacks almost
                // all the time; commented out in favour of a simple timeout handled by BatikApplication 

                //                if (html.contains( "<a " )) {
                //                    callbackId = "FlowText-" + hashCode();
                //                    UIUtils.activateCallback( callbackId );
                //                    log.warn( "ACTIVATE: " + callbackId );
                //                }
            }

            //            @Override
            //            public void dispose() {
            //                super.dispose();
            //                if (callbackId != null) {
            //                    UIUtils.deactivateCallback( callbackId );                    
            //                }
            //            }            
        };
        adapt(result, false, false);

        if (text != null) {
            result.setText(text);
        }
        return result;
    }

    @Override
    public String markdownToHtml(String markdown, Widget widget) {
        LinkRenderer linkRenderer = new DelegatingLinkRenderer(widget);
        String processed = new PegDownProcessor().markdownToHtml(markdown, linkRenderer);
        return processed;
    }

    @Override
    public Label createFlowText(Composite parent, String text, ILinkAction[] linkActions, int... styles) {
        throw new RuntimeException("not yet implemented");
    }

    /**
     * 
     */
    protected static class PegDownRenderOutput extends MarkdownRenderOutput {

        public Rendering createRendering() {
            Rendering rendering = new Rendering(url.get(), text.get());
            title.ifPresent(v -> rendering.withAttribute("title", FastEncoder.encode(v)));
            id.ifPresent(v -> rendering.withAttribute("id", FastEncoder.encode(v)));
            clazz.ifPresent(v -> rendering.withAttribute("class", FastEncoder.encode(v)));
            target.ifPresent(v -> rendering.withAttribute("target", FastEncoder.encode(v)));
            return rendering;
        }
    }

    /**
     * Delegates link and image rendering to the
     * {@link DefaultToolkit#registerMarkdownRenderer(IPanelToolkit.IMarkdownRenderer)
     * registered} {@link IMarkdownRenderer}s.
     */
    protected class DelegatingLinkRenderer extends LinkRenderer {

        private Widget widget;

        public DelegatingLinkRenderer(Widget widget) {
            this.widget = widget;
        }

        protected Rendering render(IMarkdownNode node) {
            Rendering result = null;
            // check registered factories
            for (Callable<IMarkdownRenderer> factory : mdRendererFactories) {
                PegDownRenderOutput out = new PegDownRenderOutput();
                try {
                    if (factory.call().render(DefaultToolkit.this, node, out, widget)) {
                        result = out.createRendering();
                        break;
                    }
                } catch (Exception e) {
                    throw Throwables.propagate(e);
                }
            }
            // default: simple external link
            if (result == null) {
                PegDownRenderOutput out = new PegDownRenderOutput();
                if (new ExternalLinkRenderer().render(DefaultToolkit.this, node, out, widget)) {
                    result = out.createRendering();
                }
            }
            return result;
        }

        @Override
        public Rendering render(final ExpImageNode imageNode, final String text) {
            Rendering result = render(new IMarkdownNode() {
                @Override
                public IMarkdownNode.Type type() {
                    return IMarkdownNode.Type.ExpImage;
                }

                @Override
                public String url() {
                    return imageNode.url;
                }

                @Override
                public String title() {
                    return imageNode.title;
                }

                @Override
                public String text() {
                    return text;
                }
            });
            return result != null ? result : super.render(imageNode, text);
        }

        @Override
        public Rendering render(final ExpLinkNode linkNode, final String linktext) {
            Rendering result = render(new IMarkdownNode() {
                @Override
                public IMarkdownNode.Type type() {
                    return IMarkdownNode.Type.ExpLink;
                }

                @Override
                public String url() {
                    return linkNode.url;
                }

                @Override
                public String title() {
                    return linkNode.title;
                }

                @Override
                public String text() {
                    return linktext;
                }
            });
            return result != null ? result : super.render(linkNode, linktext);
        }

        @Override
        public Rendering render(AutoLinkNode node) {
            return super.render(node);
        }

        @Override
        public Rendering render(RefLinkNode node, String url, String title, String text) {
            return super.render(node, url, title, text);
        }

        @Override
        public Rendering render(WikiLinkNode node) {
            return super.render(node);
        }
    };

    @Override
    public Link createLink(Composite parent, String text, int... styles) {
        Link result = adapt(new Link(parent, stylebits(styles)), false, false);
        if (text != null) {
            result.setText(text.contains("<a>") ? text : Joiner.on("").join("<a>", text, "</a>"));
        }
        //        Label result = createLabel( parent, text, styles | SWT.L );
        //        result.setCursor( new Cursor( Polymap.getSessionDisplay(), SWT.CURSOR_HAND ) );
        //        result.setForeground( Graphics.getColor( 0x00, 0x00, 0xff ) );
        return result;
    }

    @Override
    public Button createButton(Composite parent, String text, int... styles) {
        Button control = new Button(parent, stylebits(styles)) {
            @Override
            public void setText(String _text) {
                super.setText(StringUtils.upperCase(_text, Polymap.getSessionLocale()));
            }
        };
        if (text != null) {
            control.setText(text);
        }
        return adapt(control, true, true);
    }

    @Override
    public Text createText(Composite parent, String defaultText, int... styles) {
        Text control = adapt(new Text(parent, stylebits(styles)), true, true);
        if (defaultText != null) {
            control.setText(defaultText);
        }
        return control;
    }

    @Override
    public ActionText createActionText(Composite parent, String defaultText, int... styles) {
        ActionText result = new ActionText(parent, defaultText, styles);
        adapt(result.getText(), true, true);
        return result;
    }

    @Override
    public Composite createComposite(Composite parent, int... styles) {
        assert !contains(styles, SWT.V_SCROLL)
                && !contains(styles, SWT.H_SCROLL) : "Use createScrolledComposite()!";
        Composite result = new Composite(parent, stylebits(styles));
        result.setLayout(new FillLayout(SWT.HORIZONTAL));
        return adapt(result);
    }

    @Override
    public ScrolledComposite createScrolledComposite(Composite parent, int... styles) {
        assert contains(styles, SWT.V_SCROLL) || contains(styles, SWT.H_SCROLL);

        ScrolledComposite scrolled = new ScrolledComposite(parent, stylebits(styles));
        scrolled.setExpandHorizontal(true);
        scrolled.setExpandVertical(true);

        Composite body = createComposite(scrolled);
        body.setLayout(new FillLayout(SWT.VERTICAL));
        scrolled.setContent(body);

        scrolled.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent ev) {
                Rectangle clientArea = scrolled.getClientArea();
                int scrollbarWidth = scrolled.getVerticalBar() != null ? scrolled.getVerticalBar().getSize().x : 0;
                Point preferred = scrolled.getContent().computeSize(clientArea.width - scrollbarWidth, SWT.DEFAULT);
                scrolled.setMinSize(preferred);
            }
        });
        return adapt(scrolled);
    }

    @Override
    public Section createSection(Composite parent, String title, int... styles) {
        Section result = adapt(new Section(parent, stylebits(styles) | SWT.NO_FOCUS));
        result.setBackgroundMode(SWT.INHERIT_NONE);
        result.setText(title);
        result.setExpanded(true);

        result.addExpansionListener(new ExpansionAdapter() {
            @Override
            public void expansionStateChanged(ExpansionEvent ev) {
                // trigger resize handler of the ScrollableComposite of the panel
                Point panelSize = panelPage.control.getSize();
                panelPage.control.setSize(panelSize.x, panelSize.y + 1);
            }
        });

        result.setMenu(parent.getMenu());
        //        if (result.toggle != null) {
        //            section.toggle.setHoverDecorationColor(colors
        //                    .getColor(IFormColors.TB_TOGGLE_HOVER));
        //            section.toggle.setDecorationColor(colors
        //                    .getColor(IFormColors.TB_TOGGLE));
        //        }

        //        result.setFont( boldFontHolder.getBoldFont(parent.getFont()));

        //        if ((sectionStyle & Section.TITLE_BAR) != 0
        //                || (sectionStyle & Section.SHORT_TITLE_BAR) != 0) {
        //            colors.initializeSectionToolBarColors();
        //            result.setTitleBarBackground( colors.getColor( IFormColors.TB_BG ) );
        //            result.setTitleBarBorderColor( colors.getColor( IFormColors.TB_BORDER ) );
        //        }
        // call setTitleBarForeground regardless as it also sets the label color
        //        result.setTitleBarForeground( colors.getColor( IFormColors.TB_TOGGLE ) );

        //        FontData[] defaultFont = parent.getFont().getFontData();
        //        FontData bold = new FontData(defaultFont[0].getName(), defaultFont[0].getHeight(), SWT.BOLD);
        //        result.setFont( Graphics.getFont( bold ) );

        result.setTitleBarForeground(COLOR_SECTION_TITLE_FG.get());
        result.setTitleBarBackground(COLOR_SECTION_TITLE_BG.get());
        result.setTitleBarBorderColor(COLOR_SECTION_TITLE_BORDER.get());

        Composite client = createComposite(result);
        result.setClient(client);

        FillLayout layout = new FillLayout(SWT.VERTICAL);
        layout.spacing = 1;
        layout.marginWidth = 2;
        layout.marginHeight = 2;
        client.setLayout(layout);
        return result;
    }

    @Override
    public IPanelSection createPanelSection(Composite parent, String title, int... styles) {
        PanelSection result = new PanelSection(this, parent, styles);
        if (title != null) {
            result.setTitle(title);
        }
        return result;
    }

    @Override
    public List createList(Composite parent, int... styles) {
        List result = adapt(new List(parent, stylebits(styles)), false, false);
        return result;
    }

    @Override
    public IBusyIndicator busyIndicator(Composite parent) {
        return new BusyIndicator(parent);
    }

    @Override
    public SimpleDialog createSimpleDialog(String title) {
        return new SimpleDialog().title.put(title);
    }

    public Snackbar createSnackbar(Snackbar.Appearance appearance, String message, Item... actions) {
        return new Snackbar(this, panelPage.control).appearance.put(appearance).message.put(message).actions
                .put(actions);
    }

    public <T extends Composite> T adapt(T composite) {
        UIUtils.setVariant(composite, CSS_PREFIX);

        //        composite.setBackground( colors.getBackground() );
        //        composite.addMouseListener( new MouseAdapter() {
        //            public void mouseDown( MouseEvent e ) {
        //                ((Control)e.widget).setFocus();
        //            }
        //        } );
        //        if (composite.getParent() != null) {
        //            composite.setMenu( composite.getParent().getMenu() );
        //        }
        return composite;
    }

    @Override
    public <T extends Control> T adapt(T control, boolean trackFocus, boolean trackKeyboard) {
        return _adapt(control, trackFocus, trackKeyboard);
    }

    public static <T extends Control> T _adapt(T control, boolean trackFocus, boolean trackKeyboard) {
        UIUtils.setVariant(control, CSS_PREFIX);

        control.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
        control.setData(RWT.TOOLTIP_MARKUP_ENABLED, Boolean.TRUE);
        control.setData(MarkupValidator.MARKUP_VALIDATION_DISABLED, Boolean.TRUE);

        //        control.setBackground( colors.getBackground() );
        //        control.setForeground( colors.getForeground() );

        //        if (control instanceof ExpandableComposite) {
        //            ExpandableComposite ec = (ExpandableComposite)control;
        //            if (ec.toggle != null) {
        //                if (trackFocus)
        //                    ec.toggle.addFocusListener( visibilityHandler );
        //                if (trackKeyboard)
        //                    ec.toggle.addKeyListener( keyboardHandler );
        //            }
        //            if (ec.textLabel != null) {
        //                if (trackFocus)
        //                    ec.textLabel.addFocusListener( visibilityHandler );
        //                if (trackKeyboard)
        //                    ec.textLabel.addKeyListener( keyboardHandler );
        //            }
        //            return;
        //        }

        //        if (trackFocus) {
        //            control.addFocusListener( visibilityHandler );
        //        }
        //        if (trackKeyboard) {
        //            control.addKeyListener( keyboardHandler );
        //        }
        return control;
    }

    protected int stylebits(int... styles) {
        int result = SWT.NONE;
        for (int style : styles) {
            result |= style;
        }
        return result;
    }

    protected int styleHas(int[] styles, int search) {
        return ArrayUtils.contains(styles, search) ? search : SWT.NONE;
    }

}