ch.unifr.pai.twice.layout.client.eclipseLayout.MiceSplitLayoutPanel.java Source code

Java tutorial

Introduction

Here is the source code for ch.unifr.pai.twice.layout.client.eclipseLayout.MiceSplitLayoutPanel.java

Source

package ch.unifr.pai.twice.layout.client.eclipseLayout;

/*
 * Copyright 2013 Oliver Schmid
 * 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.
 */
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import ch.unifr.pai.twice.layout.client.commons.MiceDialogCaption;
import ch.unifr.pai.twice.layout.client.commons.ResizableDecoratorPanel;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ResizeLayoutPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * @author Oliver Schmid
 * 
 */
public class MiceSplitLayoutPanel extends SplitLayoutPanel {

    private final Map<ResizableDecoratorPanel, Widget> slots = new HashMap<ResizableDecoratorPanel, Widget>();
    private ResizableDecoratorPanel centralDecoratorPanel;
    private boolean isInFullscreenMode = false;

    public MiceSplitLayoutPanel() {
        super(5);
    }

    /**
     * Removes a widget from the split layout panel. If the widget is not the center tab panel, it can be removed directly. Otherwise, it is checked if there
     * are other widgets in the split panel which can be used as the new center widget (because a layout panel always has to contain a center widget),
     * afterwards the tab panel can be removed. If no other tab panels are around which can be made the new center widget, the whole split layout panel is
     * removed from its parent unless it is the root split layout panel (what results in a warning)
     * 
     * @see com.google.gwt.user.client.ui.SplitLayoutPanel#remove(com.google.gwt.user.client.ui.Widget)
     */
    @Override
    public boolean remove(Widget child) {
        if (getWidgetDirection(child.getParent()) != Direction.CENTER) {
            setWidgetMinSize(child.getParent(), 0);
            setWidgetSize(child.getParent(), 0);
            return super.remove(child.getParent());
        } else {
            Widget lastNonCenterWidget = getLastNonCenterWidget();
            if (lastNonCenterWidget instanceof MiceLayoutTabPanel
                    || lastNonCenterWidget instanceof MiceSplitLayoutPanel) {
                Widget originParent = lastNonCenterWidget.getParent();
                ((ResizeLayoutPanel) getCenter()).setWidget(lastNonCenterWidget);
                setWidgetSize(originParent, 0);
                setWidgetMinSize(originParent, 0);
                return super.remove(originParent);

            } else if (getParentMiceSplitLayoutPanel() != null) {
                return getParentMiceSplitLayoutPanel().remove(this);
            } else {
                Window.alert("You've tried to remove the last element - this is not allowed");
                return false;
            }

        }
    }

    /**
     * @return the last added widget which is not the center widget or null if there is none.
     */
    public Widget getLastNonCenterWidget() {
        if (getWidgetCount() < 2)
            return null;
        for (int i = getWidgetCount() - 2; i >= 0; i--) {
            if (getWidget(i) instanceof ResizeLayoutPanel)
                return getChildTabPanelFromDirectChild((ResizeLayoutPanel) getWidget(i));
        }
        return null;

    }

    /**
     * Adds a widget to the panel. If the widget is a {@link ResizeLayoutPanel}, this means that it is a container and therefore should be added directly.
     * Otherwise it has to be wrapped with such a {@link ResizeLayoutPanel} to ensure, that it is resizing accordingly to changing dimensions.
     * 
     * If the widget that shall be added is {@link MiceSplitLayoutPanel} or a {@link MiceLayoutTabPanel}, it can be added to the newly created
     * {@link ResizeLayoutPanel} directly. For all other widgets, a new {@link MiceLayoutTabPanel} has to be introduced and the widget (or if it is a
     * {@link ComplexPanel} the children of the widget) has/have to be added to this tab.
     * 
     * @see com.google.gwt.user.client.ui.SplitLayoutPanel#insert(com.google.gwt.user.client.ui.Widget, com.google.gwt.user.client.ui.DockLayoutPanel.Direction,
     *      double, com.google.gwt.user.client.ui.Widget)
     */
    @Override
    public void insert(Widget child, Direction direction, double size, Widget before) {
        if (child instanceof ResizeLayoutPanel) {
            super.insert(child, direction, size, before);
        } else {
            ResizeLayoutPanel panel = new ResizeLayoutPanel();

            if (child instanceof MiceSplitLayoutPanel || child instanceof MiceLayoutTabPanel) {
                panel.setWidget(child);
                super.insert(panel, direction, size, before);
            } else {
                MiceLayoutTabPanel slot = new MiceLayoutTabPanel(20);
                if (child instanceof ComplexPanel) {
                    ComplexPanel p = (ComplexPanel) child;
                    for (int i = p.getWidgetCount(); i > 0; i--) {
                        slot.add(p.getWidget(i - 1));
                    }
                    if (slot.getWidgetCount() > 0)
                        slot.selectTab(0);
                } else {
                    slot.add(child);
                }
                panel.setWidget(slot);
                super.insert(panel, direction, size, before);
            }
        }
    }

    private void setWidgetToSlot(ResizableDecoratorPanel slot, Widget w) {
        DockLayoutPanel layout = new DockLayoutPanel(Unit.PX);
        layout.addNorth(createHeader(slot, w), 30);
        layout.add(w);
        slot.setWidget(layout);
        slots.put(slot, w);
    }

    /**
     * Creates the header widget for a slot (including the icons for dialog, fullscreen or no-fullscreen mode.
     * 
     * @param slot
     * @param w
     * @return
     */
    private HorizontalPanel createHeader(final ResizableDecoratorPanel slot, Widget w) {
        HorizontalPanel header = new HorizontalPanel();
        header.setHeight("100%");
        header.setWidth("100%");
        header.setStyleName("miceWidgetCaption");

        if (!isInFullscreenMode) {
            Image dialogButton = new Image(GWT.getModuleBaseURL() + "images/dialog.png");
            dialogButton.addStyleName("miceWidgetButton");
            dialogButton.addClickHandler(new ClickHandler() {

                @Override
                public void onClick(ClickEvent event) {
                    toDialog(slot);
                }
            });

            header.add(dialogButton);
            Image fullscreenButton = new Image(GWT.getModuleBaseURL() + "images/fullscreen.png");
            fullscreenButton.addStyleName("miceWidgetButton");
            fullscreenButton.addClickHandler(new ClickHandler() {

                @Override
                public void onClick(ClickEvent event) {
                    makeFullScreen(slot);
                }
            });
            header.add(fullscreenButton);
        } else {
            Image nofullscreenButton = new Image(GWT.getModuleBaseURL() + "images/nofullscreen.png");
            nofullscreenButton.addStyleName("miceWidgetButton");
            nofullscreenButton.addClickHandler(new ClickHandler() {

                @Override
                public void onClick(ClickEvent event) {
                    releaseFullScreen();
                }
            });
            header.add(nofullscreenButton);
        }
        //
        // Image closeButton = new Image(GWT.getModuleBaseURL()
        // + "images/close_hover.png");
        // closeButton.addStyleName("miceWidgetButton");
        // header.add(closeButton);

        return header;
    }

    private Widget currentCenterWidget;
    /**
     * The original slot of the current full screen widget. This is used to reattach the current fullscreen widget back to the root panel
     */
    private ResizableDecoratorPanel originSlotOfCurrentFullscreenWidget;
    /**
     * Stores the size of the widgets before switching to fullscreen mode for being able to reset these when releaseing fullscreen mode again.
     */
    private final Map<Widget, Integer> sizeBeforeFullscreen = new HashMap<Widget, Integer>();

    /**
     * Minimizes the current component which is displayed in fullscreen mode. If the layout is not in fullscreen mode, this method does nothing.
     */
    private void releaseFullScreen() {
        if (isInFullscreenMode) {
            isInFullscreenMode = false;
            Widget minimizeWidget = slots.get(getCenter());
            setWidgetToSlot(originSlotOfCurrentFullscreenWidget, minimizeWidget);
            setWidgetToSlot((ResizableDecoratorPanel) getCenter(), currentCenterWidget);
            for (Widget widget : getChildren()) {
                if (getWidgetDirection(widget) != Direction.CENTER)
                    setWidgetSize(widget, sizeBeforeFullscreen.get(widget));
            }
            onResize();
            currentCenterWidget = null;
            originSlotOfCurrentFullscreenWidget = null;
            sizeBeforeFullscreen.clear();
        }

    }

    /**
     * Sets the current decorator panel (the wrapper around the tabs) to fullscreen if the application is not yet in fullscreen mode - otherwise it does
     * nothing.
     * 
     * @param w
     */
    private void makeFullScreen(ResizableDecoratorPanel w) {
        if (!isInFullscreenMode) {
            isInFullscreenMode = true;
            currentCenterWidget = slots.get(getCenter());
            for (Widget widget : getChildren()) {
                sizeBeforeFullscreen.put(widget, getSizeForWidget(widget));
            }
            setWidgetToSlot(centralDecoratorPanel, slots.get(w));
            originSlotOfCurrentFullscreenWidget = w;
            for (Widget widget : getChildren()) {
                if (this.getWidgetDirection(widget) != Direction.CENTER)
                    this.setWidgetSize(widget, 0);
            }
            centralDecoratorPanel.onResize();
        }
    }

    /**
     * Stores the width of the widget that is sent to the dialog to recover the size settings if the dialog is closed again
     */
    private final Map<ResizableDecoratorPanel, Integer> widthOfDialogsOriginalSlots = new HashMap<ResizableDecoratorPanel, Integer>();
    /**
     * Currently opened dialogs
     */
    private final Map<DialogBox, ResizableDecoratorPanel> dialogs = new HashMap<DialogBox, ResizableDecoratorPanel>();
    /**
     * Stores the size of the slots before the widget is sent to the dialog to recover the size settings if the dialog is closed again
     */
    private final Map<ResizableDecoratorPanel, Integer> originsOfCenterReplacements = new LinkedHashMap<ResizableDecoratorPanel, Integer>();

    /**
     * Close the dialog and adds the widget to the previous panel
     * 
     * @param dialog
     */
    private void closeDialog(DialogBox dialog) {
        ResizableDecoratorPanel panel = dialogs.remove(dialog);
        if (getWidgetDirection(panel) == Direction.CENTER) {
            if (originsOfCenterReplacements.size() > 0) {
                ResizableDecoratorPanel lastReplacement = null;
                Iterator<ResizableDecoratorPanel> i = originsOfCenterReplacements.keySet().iterator();
                while (i.hasNext())
                    lastReplacement = i.next();
                if (lastReplacement != null) {
                    setWidgetToSlot(lastReplacement, slots.get(getCenter()));
                    setWidgetSize(lastReplacement, originsOfCenterReplacements.get(lastReplacement));
                }
            }
        }
        setWidgetToSlot(panel, dialog.getWidget());
        if (getWidgetDirection(panel) != Direction.CENTER) {
            setWidgetSize(panel, widthOfDialogsOriginalSlots.remove(panel));
        }
        dialog.hide();
        onResize();
    }

    /**
     * @return the lastly added slot which is not a dialog. This is needed to reattach the dialog to the root layout
     */
    private ResizableDecoratorPanel getLastAddedNonDialogSlot() {
        for (int i = getChildren().size(); i > 0; i--) {
            Widget w = getChildren().get(i - 1);
            if (w instanceof ResizableDecoratorPanel) {
                if (!dialogs.containsValue(w)) {
                    return ((ResizableDecoratorPanel) w);
                }
            }
        }
        return null;
    }

    /**
     * Removes the widget from the root layout and presents it in a dialog box.
     * 
     * @param w
     */
    private void toDialog(ResizableDecoratorPanel w) {
        if (isInFullscreenMode) {
            w = originSlotOfCurrentFullscreenWidget;
            releaseFullScreen();
        }
        final ResizableDecoratorPanel finalSlot = w;
        MiceDialogCaption caption;
        final DialogBox dbox = new DialogBox(false, false, (caption = new MiceDialogCaption()));
        caption.setHandlers(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                closeDialog(dbox);
                makeFullScreen(finalSlot);
            }
        }, new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                closeDialog(dbox);
            }
        });
        dialogs.put(dbox, w);
        dbox.setPopupPosition(w.getAbsoluteLeft(), w.getAbsoluteTop());
        Widget element = slots.get(w);
        dbox.setWidget(element);
        widthOfDialogsOriginalSlots.put(w, getSizeForWidget(w));
        dbox.show();
        if (getWidgetDirection(w) == Direction.CENTER) {
            ResizableDecoratorPanel lastAddedDecoratorPanel = getLastAddedNonDialogSlot();
            if (lastAddedDecoratorPanel != null) {
                setWidgetToSlot((ResizableDecoratorPanel) getCenter(), slots.get(lastAddedDecoratorPanel));
                setWidgetSize(lastAddedDecoratorPanel, 0);
                originsOfCenterReplacements.put(lastAddedDecoratorPanel, getSizeForWidget(lastAddedDecoratorPanel));
            }
        } else {
            setWidgetSize(w, 0);
        }
        onResize();
    }

    /**
     * @param w
     * @return the size of the widget in pixels. Depending on the layouting position, this is either the widget's width (for horizontal orientation) or height
     *         (for vertical orientation).
     */
    private Integer getSizeForWidget(Widget w) {
        switch (this.getWidgetDirection(w)) {
        case EAST:
        case WEST:
            return w.getOffsetWidth();
        }
        return w.getOffsetHeight();
    }

    /**
     * @return the parent split layout panel or null if this is the root split layout panel
     */
    public MiceSplitLayoutPanel getParentMiceSplitLayoutPanel() {
        if (getParent().getParent() instanceof MiceSplitLayoutPanel)
            return (MiceSplitLayoutPanel) getParent().getParent();
        else
            return null;
    }

    /**
     * @param w
     * @return the tab panel of the given {@link ResizeLayoutPanel}
     */
    private Widget getChildTabPanelFromDirectChild(ResizeLayoutPanel w) {
        return ((SimplePanel) w).getWidget();
    }
}