org.lucidj.browser.BrowserView.java Source code

Java tutorial

Introduction

Here is the source code for org.lucidj.browser.BrowserView.java

Source

/*
 * Copyright 2018 NEOautus Ltd. (http://neoautus.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.lucidj.browser;

import com.vaadin.event.LayoutEvents;
import com.vaadin.event.ShortcutAction;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.MarginInfo;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.Accordion;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;

import com.vaadin.annotations.StyleSheet;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

import org.lucidj.api.stddef.Aggregate;
import org.lucidj.api.artdeployer.Artifact;
import org.lucidj.api.artdeployer.ArtifactDeployer;
import org.lucidj.api.artdeployer.BundleManager;
import org.lucidj.api.core.ComponentManager;
import org.lucidj.api.core.ComponentState;
import org.lucidj.api.core.EmbeddingContext;
import org.lucidj.api.core.SerializerEngine;
import org.lucidj.api.core.ServiceContext;
import org.lucidj.api.vui.ApplicationInterface;
import org.lucidj.api.vui.RendererFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@StyleSheet("vaadin://~/Browser_libraries/styles.css") // <-- does this works??
public class BrowserView extends VerticalLayout implements View, ApplicationInterface {
    private final static Logger log = LoggerFactory.getLogger(BrowserView.class);
    private final static String view_name = "browse";
    private final static String nav_rex = "^" + view_name + "\\/([\\-._a-zA-Z0-9]+)\\/(.+)";
    public final static Pattern NAV_PATTERN = Pattern.compile(nav_rex);

    private static final String VM_NOTEBOOK = "view-mode-notebook";
    private static final String VM_SINGLE = "view-mode-single";
    private static final String VM_ZOOM_CODE = "view-mode-zoom-code";
    private static final String VM_ZOOM_EXEC = "view-mode-zoom-exec";

    private String caption = "Browse";
    private CssLayout current_toolbar = null;
    private Accordion acSidebar = null;
    private ComponentPalette sidebar = null;

    private ServiceContext serviceContext;
    private BundleContext ctx;
    private ComponentManager componentManager;
    private BundleManager bundleManager;
    private ArtifactDeployer artifactDeployer;
    private RendererFactory rendererFactory;

    private long last_save = 0;
    private boolean embedding_changed = false;

    private Object root_object = null;
    private File root_source = null;
    private List object_list = null;
    private Object current_object;
    private Map<Object, Cell> active_cells = new ConcurrentHashMap<>();

    private VerticalLayout content;
    private Cell insert_here_cell;
    private SerializerEngine serializer;

    public BrowserView(ServiceContext serviceContext, BundleContext bundleContext) {
        this.serviceContext = serviceContext;
        ctx = bundleContext;
        serializer = serviceContext.getService(bundleContext, SerializerEngine.class);
        componentManager = serviceContext.getService(bundleContext, ComponentManager.class);
        bundleManager = serviceContext.getService(bundleContext, BundleManager.class);
        artifactDeployer = serviceContext.getService(bundleContext, ArtifactDeployer.class);
        rendererFactory = serviceContext.getService(bundleContext, RendererFactory.class);
    }

    @Override // ApplicationInterface
    public AbstractComponent getToolbar() {
        return (current_toolbar);
    }

    @Override // ApplicationInterface
    public AbstractComponent getSidebar() {
        return (acSidebar);
    }

    @Override // ApplicationInterface
    public String getCaption() {
        return (caption);
    }

    class Cell extends AbstractCell {
        public Cell(Object object) {
            // Cell formatting, decoration and event handling is located at AbstractCell
            super(rendererFactory, object);
        }

        @Override
        public Object insertNewObjectBefore(String obj_canonical_name, Object ref_obj) {
            Object new_object = insert_new_cell(obj_canonical_name, object_list.indexOf(ref_obj));
            update_cell_focus(new_object, true);
            return (new_object);
        }

        @Override
        public Object insertNewObjectAfter(String obj_canonical_name, Object ref_obj) {
            Object new_object = insert_new_cell(obj_canonical_name, object_list.indexOf(ref_obj) + 1);
            update_cell_focus(new_object, true);
            return (new_object);
        }

        @Override
        public void moveObjectBefore(Object source_object, Object target_object) {
            object_list.remove(source_object);
            object_list.add(object_list.indexOf(target_object), source_object);
            synchronize_cell_view();
            update_cell_focus(source_object, false);
        }

        @Override
        public void moveObjectAfter(Object source_object, Object target_object) {
            object_list.remove(source_object);
            object_list.add(object_list.indexOf(target_object) + 1, source_object);
            synchronize_cell_view();
            update_cell_focus(source_object, false);
        }

        @Override
        public void taskStateClick(int task_state) {
            ComponentState cs;

            if ((cs = Aggregate.adapt(ComponentState.class, getSourceObject())) != null) {
                if (task_state == ComponentState.RUNNING) {
                    cs.signal(ComponentState.SIGTERM);
                } else // TODO: Check more states??
                {
                    cs.signal(ComponentState.SIGSTART);
                }
            }
        }

        @Override
        public void layoutClick(Component component) {
            update_cell_focus(getSourceObject(), false);
        }

        @Override
        public void layoutDoubleClick(Component component) {
            // Nothing for now
        }

        @Override
        public void setToolbar(AbstractLayout toolbar) {
            // Remove any existing toolbar
            for (Component c : current_toolbar) {
                if (Cell.class.getCanonicalName().equals(c.getId())) {
                    current_toolbar.removeComponent(c);
                    break;
                }
            }

            // Add new toolbar, if we have one
            if (toolbar != null) {
                toolbar.setId(Cell.class.getCanonicalName());
                current_toolbar.addComponent(toolbar);
            }
        }
    }

    private void update_cell_focus(Object focus_object, boolean scroll_into_view) {
        // Set focus to null selects the first object
        if (focus_object == null && object_list.size() > 0) {
            current_object = object_list.get(0);
        } else {
            current_object = focus_object;
        }

        for (Object object_ref : object_list) {
            Cell cell = active_cells.get(object_ref);

            if (object_ref == current_object) {
                cell.setFocus();

                if (scroll_into_view) {
                    cell.scrollIntoView();
                }
            } else {
                cell.removeFocus();
            }
        }
    }

    private void synchronize_cell_view() {
        log.info("synchronize_cell_view: START");

        //------------------------------------------------
        // Create, insert and move active cells on layout
        //------------------------------------------------
        int cell_index = 0;

        log.info("synchronize_cell_view: will create/insert/move {} cells", object_list.size());

        for (cell_index = 0; cell_index < object_list.size(); cell_index++) {
            Object source_object = object_list.get(cell_index);
            Cell cell = active_cells.get(source_object);

            log.info("synchronize_cell_view: active_cells source_object={} cell={}", source_object, cell);

            if (cell == null) {
                cell = new Cell(source_object);
                active_cells.put(source_object, cell);
                log.info("synchronize_cell_view: NEW cell source_object={} cell={}", source_object, cell);
            }

            log.info("synchronize_cell_view: content[{}]={} decorated={}", cell_index,
                    cell_index < content.getComponentCount() ? content.getComponent(cell_index) : null,
                    cell.getDecoratedCell());

            if (cell_index >= content.getComponentCount()
                    || content.getComponent(cell_index) != cell.getDecoratedCell()) {
                log.info("synchronize_cell_view: addComponent {} into #{}", cell.getDecoratedCell(), cell_index);
                content.addComponent(cell.getDecoratedCell(), cell_index);
            }
        }

        //--------------------------------
        // Remove extra cells from layout
        //--------------------------------
        while (content.getComponentCount() > object_list.size()) {
            log.info("synchronize_cell_view: DELETE content.count={} object_list.size={}",
                    content.getComponentCount(), object_list.size());
            content.removeComponent(content.getComponent(object_list.size()));
        }

        //-----------------------------------
        // Delete unused cells from cell map
        //-----------------------------------
        for (Object object_ref : active_cells.keySet()) {
            Cell cell = active_cells.get(object_ref);

            log.info("synchronize_cell_view: ref={} cell={} decorated={} parent={}", object_ref, cell,
                    cell.getDecoratedCell(), cell.getDecoratedCell().getParent());

            if (cell.getDecoratedCell().getParent() == null) {
                log.info("synchronize_cell_view: REMOVE ref={} cell={}", object_ref, cell);
                active_cells.remove(object_ref);
            }
        }

        //------------------------------------------
        // No cells? Leave an "insert here" message
        //------------------------------------------
        if (object_list.size() == 0) {
            content.addComponent(insert_here_cell.getDecoratedCell());
        } else {
            content.removeComponent(insert_here_cell.getDecoratedCell());
        }

        log.info("synchronize_cell_view: COMPLETE");
    }

    private Object insert_new_cell(String canonical_name, int index) {
        Object new_object = insert_new_object(canonical_name, index);
        synchronize_cell_view();
        return (new_object);
    }

    private Object insert_new_object(String canonical_name, int index) {
        log.info("insert_new_object: canonical_name={} index={}", canonical_name, index);

        // TODO: HANDLE NPE WHEN OBJECT CLASS DOESN'T EXISTS
        Object object = serviceContext.newServiceObject(canonical_name);

        log.info("*** insert_new_object: object={}", object);

        if (index == -1) {
            object_list.add(object);
        } else {
            object_list.add(index, object);
        }

        return (object);
    }

    //-----------------------------------------------------------------------------------------------------------------
    // OLD METHODS....

    private int get_current_cell_index() {
        if (current_object != null) {
            return (object_list.indexOf(current_object));
        }

        return (-1);
    }

    private void set_current_cell_index(int index) {
        if (index >= 0 && index < object_list.size()) {
            current_object = object_list.get(index);
            update_cell_focus(current_object, true);
        }
    }

    private void handle_button_click(Button source) {
        switch (source.getId()) {
        case "save": {
            save_embedding();
            break;
        }
        //            case VM_NOTEBOOK:
        //            {
        //                // TODO: CREATE BETTER VIEW REPRESENTATION/REFERENCE
        //                UI.getCurrent ().getNavigator ().navigateTo (view_name + ":" +
        //                        ____task_source + "/" +
        //                        VM_NOTEBOOK);
        //                break;
        //            }
        //            case VM_SINGLE:
        //            {
        //                UI.getCurrent ().getNavigator ().navigateTo (view_name + ":" +
        //                        ____task_source + "/" +
        //                        VM_SINGLE + "/" +
        //                        get_current_cell_index ());
        //                break;
        //            }
        case "prev-smartbox": {
            set_current_cell_index(get_current_cell_index() - 1);
            break;
        }
        case "next-smartbox": {
            set_current_cell_index(get_current_cell_index() + 1);
            break;
        }
        case "delete-cell": {
            int cell_index = get_current_cell_index();

            if (cell_index != -1) {
                log.info("cell_index = {}", cell_index);
                object_list.remove(cell_index);
                synchronize_cell_view();

                if (cell_index >= object_list.size()) {
                    cell_index = object_list.size() - 1;
                }

                set_current_cell_index(cell_index);
                embedding_changed = true;
            }
            break;
        }
        case "output": {
            // TODO: FROM TOOLBAR
            //                String current_view_mode = current_cell.getViewMode();
            //                formulae_changed = true;
            //
            //                if (current_view_mode.equals ("default"))
            //                {
            //                    current_cell.setViewMode ("canvas-only");
            //                }
            //                else
            //                {
            //                    current_cell.setViewMode ("default");
            //                }
            break;
        }
        default: {
            Notification.show("Not implemented", source.getId(), Notification.Type.HUMANIZED_MESSAGE);
            break;
        }
        }
    }

    private Button createButton(Layout parent, String id, Resource icon, String caption, int kc, int mk) {
        final Button new_button = new Button();

        if (caption != null) {
            new_button.setCaption(caption);
        }
        new_button.setIcon(icon);
        new_button.addStyleName("tiny");
        new_button.addStyleName("toolbar");
        new_button.setId(id);

        new_button.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(Button.ClickEvent clickEvent) {
                handle_button_click(new_button);
            }
        });

        parent.addComponent(new_button);

        if (kc != 0) {
            new_button.addShortcutListener(new AbstractField.FocusShortcut(new_button, kc, mk) {
                @Override
                public void handleAction(Object sender, Object target) {
                    handle_button_click(new_button);
                }
            });
        }

        return (new_button);
    }

    private Button createButton(Layout parent, String id, Resource icon, String caption) {
        return (createButton(parent, id, icon, caption, 0, 0));
    }

    private Button createButton(Layout parent, String id, Resource icon) {
        return (createButton(parent, id, icon, null, 0, 0));
    }

    private void build_toolbar() {
        current_toolbar = new CssLayout();

        CssLayout local_toolbar = new CssLayout();
        current_toolbar.addComponent(local_toolbar);

        CssLayout navigation = new CssLayout();
        navigation.addStyleName("v-component-group");
        navigation.addStyleName("ui-toolbar-spacer");
        createButton(navigation, "prev-smartbox", FontAwesome.CHEVRON_LEFT, null, ShortcutAction.KeyCode.ARROW_UP,
                ShortcutAction.ModifierKey.CTRL);
        createButton(navigation, "next-smartbox", FontAwesome.CHEVRON_RIGHT, null,
                ShortcutAction.KeyCode.ARROW_DOWN, ShortcutAction.ModifierKey.CTRL);
        local_toolbar.addComponent(navigation);

        createButton(local_toolbar, "save", FontAwesome.SAVE).addStyleName("ui-toolbar-spacer");
        //        createButton (local_toolbar, "test", FontAwesome.MAGIC)
        //            .addStyleName("ui-toolbar-spacer");
        //        createButton (local_toolbar, "test2", FontAwesome.FLASK)
        //                .addStyleName("ui-toolbar-spacer");

        CssLayout edition = new CssLayout();
        edition.addStyleName("v-component-group");
        edition.addStyleName("ui-toolbar-spacer");
        createButton(edition, "undo-cell-edit", FontAwesome.UNDO, "Undo");
        createButton(edition, "redo-cell-edit", FontAwesome.REPEAT, null);
        createButton(edition, "delete-cell", FontAwesome.TRASH_O, null);
        local_toolbar.addComponent(edition);

        CssLayout view_controls = new CssLayout();
        view_controls.addStyleName("v-component-group");
        view_controls.addStyleName("ui-toolbar-spacer");
        createButton(view_controls, VM_NOTEBOOK,
                new ExternalResource("vaadin://~/Browser_libraries/notebook-view.png"));
        createButton(view_controls, VM_SINGLE, new ExternalResource("vaadin://~/Browser_libraries/single-view.png"),
                null, ShortcutAction.KeyCode.INSERT, ShortcutAction.ModifierKey.CTRL);
        local_toolbar.addComponent(view_controls);

        // TODO: CTRL+ENTER => RUN AND SKIP TO NEXT
        // TODO: SELECTION + SHIFT+ENTER => RUN ONLY SELECTED STATEMENTS

        final Button source_view = new Button();
        source_view.setId("output");
        source_view.addStyleName("ui-toolbar-spacer");
        source_view.setHtmlContentAllowed(true);
        String ico = "<path class=\"path1\" d=\"M1088 128h-64v-64c0-35.2-28.8-64-64-64h-896c-35.2 0-64 28.8-64 64v768c0 35.2 28.8 64 64 64h64v64c0 35.2 28.8 64 64 64h896c35.2 0 64-28.8 64-64v-768c0-35.2-28.8-64-64-64zM128 192v640h-63.886c-0.040-0.034-0.082-0.076-0.114-0.116v-767.77c0.034-0.040 0.076-0.082 0.114-0.114h895.77c0.040 0.034 0.082 0.076 0.116 0.116v63.884h-768c-35.2 0-64 28.8-64 64v0zM1088 959.884c-0.034 0.040-0.076 0.082-0.116 0.116h-895.77c-0.040-0.034-0.082-0.076-0.114-0.116v-767.77c0.034-0.040 0.076-0.082 0.114-0.114h895.77c0.040 0.034 0.082 0.076 0.116 0.116v767.768z\"></path>\n"
                + "<path class=\"path2\" d=\"M960 352c0 53.020-42.98 96-96 96s-96-42.98-96-96 42.98-96 96-96 96 42.98 96 96z\"></path>\n"
                + "<path class=\"path3\" d=\"M1024 896h-768v-128l224-384 256 320h64l224-192z\"></path>";
        source_view.setCaption(
                "<svg style=\"fill: currentColor; width: 1.5em; margin-top:0.3em;\" viewBox=\"0 0 1152 1024\">"
                        + ico + "</svg>");
        source_view.addStyleName("tiny");
        source_view.addStyleName("toolbar");
        source_view.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(Button.ClickEvent clickEvent) {
                handle_button_click(source_view);
            }
        });
        local_toolbar.addComponent(source_view);

        //        Button output_view = new Button ();
        //        output_view.setHtmlContentAllowed(true);
        //        String ico2 = "<path d=\"M249.649 792.806l-107.776 166.4 11.469 54.426 54.272-11.622 107.725-166.298c-11.469-6.144-22.835-12.698-33.843-19.968-11.162-7.219-21.811-14.95-31.846-22.938zM705.943 734.694c0.717-1.485 1.178-3.123 1.843-4.71 2.714-5.99 5.12-11.981 7.066-18.278 0.307-1.126 0.461-2.253 0.819-3.277 1.997-6.963 3.686-13.824 5.018-20.89 0-0.358 0-0.614 0-1.075 9.984-59.853-7.424-126.618-47.258-186.931l56.832-87.757c65.485 8.346 122.112-8.141 149.35-50.278 47.258-72.858-10.24-194.15-128.256-271.002-118.118-76.902-252.058-80.128-299.213-7.373-27.341 42.189-19.354 100.71 15.002 157.338l-56.934 87.757c-71.117-11.93-139.059-0.819-189.594 32.768-0.307 0.102-0.666 0.205-0.87 0.41-5.888 3.994-11.622 8.397-16.998 13.005-0.87 0.717-1.894 1.382-2.611 2.099-5.018 4.301-9.523 9.114-13.875 13.926-1.024 1.229-2.458 2.304-3.43 3.584-5.427 6.195-10.445 12.749-14.848 19.712-70.861 109.21-10.394 274.483 134.81 369.101 145.306 94.618 320.512 82.637 391.219-26.573 4.454-6.912 8.55-14.131 11.93-21.555zM664.215 224.845c-45.414-29.542-67.584-76.134-49.408-104.243 18.125-28.006 69.683-26.726 114.995 2.816 45.517 29.542 67.482 76.237 49.408 104.243s-69.53 26.726-114.995-2.816z\"></path>";
        //        output_view.setCaption("<svg style=\"fill: currentColor; width: 1.5em; margin-top:0.3em;\" viewBox=\"0 0 1024 1024\">" + ico2 + "</svg>");
        //        output_view.addStyleName("tiny");
        //        view_controls.addComponent (output_view);
        //
        //        Button run = new Button ();
        //        run.setHtmlContentAllowed(true);
        //        String ico3 = "<path class=\"path1\" d=\"M192 128l640 384-640 384z\"></path>";
        //        run.setCaption("<svg style=\"fill: currentColor; width: 1.5em; margin-top:0.3em;\" viewBox=\"0 0 1024 1024\">" + ico3 + "</svg>");
        //        run.addStyleName("tiny");
        //        view_controls.addComponent (run);

    }

    private void build_sidebar() {
        acSidebar = new Accordion();
        acSidebar.setSizeFull();
        acSidebar.addStyleName("borderless");

        sidebar = new ComponentPalette(componentManager.newComponentSet());
        sidebar.setWidth(100, Unit.PERCENTAGE);
        sidebar.setHeightUndefined();
        sidebar.setPaletteClickListener(new LayoutEvents.LayoutClickListener() {
            @Override
            public void layoutClick(LayoutEvents.LayoutClickEvent layoutClickEvent) {
                Component clicked = layoutClickEvent.getClickedComponent();
                String canonical_name = clicked != null ? clicked.getId() : null;

                log.info("layoutClick: DoubleClick clicked={} canonical_name={}", clicked, canonical_name);

                // Handle double-clicking a component inside the palette
                if (canonical_name != null && layoutClickEvent.isDoubleClick()) {
                    log.info("layoutClick: DoubleClick clicked={} canonical_name={}", clicked, canonical_name);
                    int cell_index = get_current_cell_index();
                    Object new_object = insert_new_cell(canonical_name, cell_index + 1);
                    update_cell_focus(new_object, true);
                }
            }
        });

        acSidebar.addTab(sidebar, "Components");

        acSidebar.addTab(new Label("Hello world"), "Visualization");
    }

    @Override
    public void detach() {
        super.detach();
        if (embedding_changed) {
            save_embedding();
        }
    }

    public boolean save_embedding() {
        if (root_source != null // root_source == null -> read-only embedding
                && !serializer.serializeObject(root_source.toPath(), root_object)) {
            return (false);
        }
        embedding_changed = false;
        return (true);
    }

    private void unfocus(Component component) {
        Component parent = component.getParent();

        while (parent != null) {
            if (parent instanceof Component.Focusable) {
                ((Component.Focusable) parent).focus();
                break;
            } else {
                parent = parent.getParent();
            }
        }
    }

    private void build_view() {
        setMargin(new MarginInfo(true, false, true, false));

        build_toolbar();
        build_sidebar();

        insert_here_cell = new Cell(null);

        // TODO: THIS BLOCK IS ACTUALLY A FILE HEADER OBJECT
        //+++
        HorizontalLayout header = new HorizontalLayout();
        header.setWidth(100, Unit.PERCENTAGE);
        {
            CssLayout left_panel = new CssLayout();
            left_panel.setWidth(40, Unit.PIXELS);
            header.addComponent(left_panel);

            final HorizontalLayout caption = new HorizontalLayout();
            caption.setSpacing(true);
            caption.addStyleName("formula-header");
            caption.setWidth(100, Unit.PERCENTAGE);
            {
                //caption.addComponent (get_icon ("freepik-saturn.png"));

                VerticalLayout title_area = new VerticalLayout();
                {
                    final TextField title = new TextField();
                    title.setWidth(100, Unit.PERCENTAGE);
                    title.setValue("Default");

                    //                    final ShortcutListener handle_enter = new ShortcutListener
                    //                        ("EnterShortcut", ShortcutAction.KeyCode.ENTER, null)
                    //                    {
                    //                        @Override
                    //                        public void handleAction (Object o, Object o1)
                    //                        {
                    //                            Notification.show ("New title: " + title.getValue());
                    //                            unfocus (title);
                    //
                    //                        }
                    //                    };
                    //
                    //                    title.addFocusListener (new FieldEvents.FocusListener ()
                    //                    {
                    //                        @Override
                    //                        public void focus (FieldEvents.FocusEvent focusEvent)
                    //                        {
                    //                            title.addShortcutListener (handle_enter);
                    //                        }
                    //                    });
                    //
                    //                    title.addBlurListener (new FieldEvents.BlurListener ()
                    //                    {
                    //                        @Override
                    //                        public void blur (FieldEvents.BlurEvent blurEvent)
                    //                        {
                    //                            title.removeShortcutListener (handle_enter);
                    //                        }
                    //                    });
                    title_area.addComponent(title);
                }

                caption.addComponent(title_area);
                caption.setExpandRatio(title_area, 1.0f);
            }
            header.addComponent(caption);

            CssLayout right_panel = new CssLayout();
            right_panel.setWidth(36, Unit.PIXELS);
            header.addComponent(right_panel);
            header.setExpandRatio(caption, 1.0f);
        }
        addComponent(header);
        //---

        content = new VerticalLayout();
        content.addStyleName("formula-cells");
        addComponent(content);

        synchronize_cell_view();

        // Set focus to first available object
        update_cell_focus(null, true);
    }

    // TODO: GET RID OF THIS CONVOLUTED BundleManager/Artifact DEPENDENCY
    public boolean init_component(ViewChangeListener.ViewChangeEvent event) {
        Matcher m = NAV_PATTERN.matcher(event.getViewName());

        if (!m.find()) {
            return (false);
        }

        String bundle_symbolic_name = m.group(1);
        String object_path = m.group(2);
        Bundle bundle = bundleManager.getBundleByDescription(bundle_symbolic_name, null);

        log.info("bundle_symbolic_name={} object_path={} bundle={}", bundle_symbolic_name, object_path, bundle);

        if (bundle == null) {
            return (false);
        }

        Artifact instance = artifactDeployer.getArtifact(bundle);

        log.info("bundle={} deployment_instance={}", bundle, instance);

        if (instance == null) {
            return (false);
        }

        EmbeddingContext ec = instance.adapt(EmbeddingContext.class);

        // Try to find a 'gluon' embedding
        if ((root_object = ec.getObject(object_path, "gluon")) == null) {
            return (false);
        }

        // Get the source location for the object
        URI object_uri = instance.getLocation(object_path);
        root_source = new File(object_uri);

        // We have a valid embedding!
        object_list = Aggregate.adapt(List.class, root_object);
        log.info("object_list: {}", object_list);
        return (true);
    }

    @Override // View
    public void enter(ViewChangeListener.ViewChangeEvent event) {
        log.info("Enter viewName=" + event.getViewName() + " parameters=" + event.getParameters());

        if (getComponentCount() == 0) {
            if (init_component(event)) {
                build_toolbar();
                build_view();
            }
        }
    }
}

// EOF