/* * Copyright 2011 John Ahlroos * * 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 fi.jasoft.dragdroplayouts.client.ui; import java.util.Iterator; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VCaption; import com.vaadin.terminal.gwt.client.ui.VTabsheet; import com.vaadin.terminal.gwt.client.ui.VTabsheetPanel; import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation; import com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler; import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCallback; import com.vaadin.terminal.gwt.client.ui.dd.VDragEvent; import com.vaadin.terminal.gwt.client.ui.dd.VDropHandler; import com.vaadin.terminal.gwt.client.ui.dd.VHasDropHandler; import fi.jasoft.dragdroplayouts.client.ui.VLayoutDragDropMouseHandler.DragStartListener; import fi.jasoft.dragdroplayouts.client.ui.interfaces.VDDTabContainer; import fi.jasoft.dragdroplayouts.client.ui.interfaces.VHasDragMode; import fi.jasoft.dragdroplayouts.client.ui.util.IframeCoverUtility; public class VDDTabSheet extends VTabsheet implements VHasDragMode, VHasDropHandler, DragStartListener, VDDTabContainer { public static final String CLASSNAME_NEW_TAB = "new-tab"; public static final String CLASSNAME_NEW_TAB_LEFT = "new-tab-left"; public static final String CLASSNAME_NEW_TAB_RIGHT = "new-tab-right"; public static final String CLASSNAME_NEW_TAB_CENTER = "new-tab-center"; public static final float DEFAULT_HORIZONTAL_DROP_RATIO = 0.2f; private LayoutDragMode dragMode = LayoutDragMode.NONE; protected VAbstractDropHandler dropHandler; protected float tabLeftRightDropRatio = DEFAULT_HORIZONTAL_DROP_RATIO; private ApplicationConnection client; protected final ComplexPanel tabBar; protected final VTabsheetPanel tabPanel; private final Element spacer; private Element currentlyEmphasised; protected final Element newTab = DOM.createDiv(); protected boolean iframeCoversEnabled = false; private final VDragFilter dragFilter = new VTabDragFilter(this); private final IframeCoverUtility iframeCoverUtility = new IframeCoverUtility(); public VDDTabSheet() { super(); newTab.setClassName(CLASSNAME_NEW_TAB); // Get the tabBar tabBar = (ComplexPanel) getChildren().get(0); // Get the content tabPanel = (VTabsheetPanel) getChildren().get(1); // Get the spacer Element tBody = tabBar.getElement(); spacer = tBody.getChild(tBody.getChildCount() - 1).getChild(0) .getChild(0).cast(); ddMouseHandler.addDragStartListener(this); } /* * (non-Javadoc) * * @see com.google.gwt.user.client.ui.Widget#onUnload() */ @Override protected void onUnload() { super.onUnload(); ddMouseHandler.detach(); iframeCoverUtility.setIframeCoversEnabled(false, getElement()); } // The drag mouse handler which handles the creation of the transferable private final VLayoutDragDropMouseHandler ddMouseHandler = new VLayoutDragDropMouseHandler( this, dragMode); /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.dd.VHasDropHandler#getDropHandler() */ public VDropHandler getDropHandler() { return dropHandler; } /* * (non-Javadoc) * * @see fi.jasoft.dragdroplayouts.client.ui.VHasDragMode#getDragMode() */ public LayoutDragMode getDragMode() { return dragMode; } /** * A hook for extended components to post process the the drop before it is * sent to the server. Useful if you don't want to override the whole drop * handler. */ protected boolean postDropHook(VDragEvent drag) { // Extended classes can add content here... return true; } /** * A hook for extended components to post process the the enter event. * Useful if you don't want to override the whole drophandler. */ protected void postEnterHook(VDragEvent drag) { // Extended classes can add content here... } /** * A hook for extended components to post process the the leave event. * Useful if you don't want to override the whole drophandler. */ protected void postLeaveHook(VDragEvent drag) { // Extended classes can add content here... } /** * A hook for extended components to post process the the over event. Useful * if you don't want to override the whole drophandler. */ protected void postOverHook(VDragEvent drag) { // Extended classes can add content here... } /** * Can be used to listen to drag start events, must return true for the drag * to commence. Return false to interrupt the drag: */ public boolean dragStart(Widget widget, LayoutDragMode mode) { Widget w = tabPanel.getWidget(getTabPosition(widget)); return dragMode != LayoutDragMode.NONE && dragFilter.isDraggable(w); } /** * Creates a drop handler if one does not already exist and updates it from * the details received from the server. * * @param childUidl * The UIDL */ protected void updateDropHandler(UIDL childUidl) { if (dropHandler == null) { dropHandler = new VAbstractDropHandler() { /* * (non-Javadoc) * * @see com.vaadin.terminal.gwt.client.ui.dd.VDropHandler# * getApplicationConnection() */ public ApplicationConnection getApplicationConnection() { return client; } /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler * #getPaintable() */ @Override public Paintable getPaintable() { return VDDTabSheet.this; } /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler * #dragAccepted * (com.vaadin.terminal.gwt.client.ui.dd.VDragEvent) */ @Override protected void dragAccepted(VDragEvent drag) { dragOver(drag); } /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler * #drop(com.vaadin.terminal.gwt.client.ui.dd.VDragEvent) */ @Override public boolean drop(VDragEvent drag) { deEmphasis(); // Update the details updateDropDetails(drag); return postDropHook(drag) && super.drop(drag); }; /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler * #dragOver(com.vaadin.terminal.gwt.client.ui.dd.VDragEvent) */ @Override public void dragOver(VDragEvent drag) { if (drag.getElementOver() == newTab) { return; } deEmphasis(); updateDropDetails(drag); postOverHook(drag); // Check if we are dropping on our self if (VDDTabSheet.this.equals(drag.getTransferable().getData( Constants.TRANSFERABLE_DETAIL_COMPONENT))) { return; } // Validate the drop validate(new VAcceptCallback() { public void accepted(VDragEvent event) { emphasis(event.getElementOver(), event); } }, drag); }; /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler * #dragLeave(com.vaadin.terminal.gwt.client.ui.dd.VDragEvent) */ @Override public void dragLeave(VDragEvent drag) { deEmphasis(); updateDropDetails(drag); postLeaveHook(drag); }; }; } // Update the rules dropHandler.updateAcceptRules(childUidl); } /** * Updates the drop details while dragging. This is needed to ensure client * side criterias can validate the drop location. * * @param widget * The container which we are hovering over * @param event * The drag event */ protected void updateDropDetails(VDragEvent event) { Element element = event.getElementOver(); if (tabBar.getElement().isOrHasChild(element)) { Widget w = Util.findWidget(element, null); if (w == tabBar) { // Ove3r the spacer // Add index event.getDropDetails().put(Constants.DROP_DETAIL_TO, tabBar.getWidgetCount() - 1); // Add drop location event.getDropDetails().put( Constants.DROP_DETAIL_HORIZONTAL_DROP_LOCATION, HorizontalDropLocation.RIGHT); } else { // Add index event.getDropDetails().put(Constants.DROP_DETAIL_TO, getTabPosition(w)); // Add drop location HorizontalDropLocation location = VDragDropUtil .getHorizontalDropLocation(element, Util .getTouchOrMouseClientX(event .getCurrentGwtEvent()), tabLeftRightDropRatio); event.getDropDetails().put( Constants.DROP_DETAIL_HORIZONTAL_DROP_LOCATION, location); } // Add mouse event details MouseEventDetails details = new MouseEventDetails( event.getCurrentGwtEvent(), getElement()); event.getDropDetails().put(Constants.DROP_DETAIL_MOUSE_EVENT, details.serialize()); } } /** * Handles drag mode changes recieved from the server * * @param uidl * The UIDL */ private void handleDragModeUpdate(UIDL uidl) { if (uidl.hasAttribute(VHasDragMode.DRAGMODE_ATTRIBUTE)) { LayoutDragMode[] modes = LayoutDragMode.values(); dragMode = modes[uidl .getIntAttribute(VHasDragMode.DRAGMODE_ATTRIBUTE)]; ddMouseHandler.updateDragMode(dragMode); if (dragMode != LayoutDragMode.NONE) { // Cover iframes if necessery iframeCoversEnabled = uidl .getBooleanAttribute(IframeCoverUtility.SHIM_ATTRIBUTE); // Listen to mouse down events ddMouseHandler.attachTo(tabBar); } else if (dragMode == LayoutDragMode.NONE) { // Remove iframe covers iframeCoversEnabled = false; // Remove mouse down handler ddMouseHandler.detach(); } } } /* * (non-Javadoc) * * @see * com.vaadin.terminal.gwt.client.ui.VTabsheet#updateFromUIDL(com.vaadin * .terminal.gwt.client.UIDL, * com.vaadin.terminal.gwt.client.ApplicationConnection) */ @Override public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { this.client = client; for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) { final UIDL childUIDL = (UIDL) it.next(); if (childUIDL.getTag().equals("-ac")) { updateDropHandler(childUIDL); } } UIDL modifiedUIDL = VDragDropUtil.removeDragDropCriteraFromUIDL(uidl); super.updateFromUIDL(modifiedUIDL, client); // Handles changes in dropHandler handleDragModeUpdate(modifiedUIDL); // Handle drop ratio settings handleCellDropRatioUpdate(modifiedUIDL); // Handle iframe covering iframeCoverUtility.setIframeCoversEnabled(iframeCoversEnabled, getElement()); // Update dragfilter dragFilter.update(uidl, client); } /** * Emphasisizes a container element * * @param element */ protected void emphasis(Element element, VDragEvent event) { boolean internalDrag = event.getTransferable().getDragSource() == this; if (tabBar.getElement().isOrHasChild(element)) { Widget w = Util.findWidget(element, null); if (w == tabBar && !internalDrag) { // Over spacer Element spacerContent = spacer.getChild(0).cast(); spacerContent.appendChild(newTab); currentlyEmphasised = element; } else if (w instanceof VCaption) { // Over a tab VCaption tab = (VCaption) w; HorizontalDropLocation location = VDragDropUtil .getHorizontalDropLocation(element, Util .getTouchOrMouseClientX(event .getCurrentGwtEvent()), tabLeftRightDropRatio); if (location == HorizontalDropLocation.LEFT) { int index = getTabPosition(w); if (index == 0) { currentlyEmphasised = tab.getElement(); currentlyEmphasised .addClassName(CLASSNAME_NEW_TAB_LEFT); } else { Widget prevTab = tabBar.getWidget(index - 1); currentlyEmphasised = prevTab.getElement(); currentlyEmphasised .addClassName(CLASSNAME_NEW_TAB_RIGHT); } } else if (location == HorizontalDropLocation.RIGHT) { tab.getElement().addClassName(CLASSNAME_NEW_TAB_RIGHT); currentlyEmphasised = tab.getElement(); } else { tab.getElement().addClassName(CLASSNAME_NEW_TAB_CENTER); currentlyEmphasised = tab.getElement(); } } } } /** * Removes any previous emphasis made by drag&drop */ protected void deEmphasis() { if (currentlyEmphasised != null && tabBar.getElement().isOrHasChild(currentlyEmphasised)) { Widget w = Util.findWidget(currentlyEmphasised, null); currentlyEmphasised.removeClassName(CLASSNAME_NEW_TAB_LEFT); currentlyEmphasised.removeClassName(CLASSNAME_NEW_TAB_RIGHT); currentlyEmphasised.removeClassName(CLASSNAME_NEW_TAB_CENTER); if (w == tabBar) { // Over spacer Element spacerContent = spacer.getChild(0).cast(); spacerContent.removeChild(newTab); } currentlyEmphasised = null; } } /** * Handles updates the the hoover zones of the tab which specifies at which * position a component is dropped over a tab * * @param uidl * The UIDL */ private void handleCellDropRatioUpdate(UIDL uidl) { if (uidl.hasAttribute("hDropRatio")) { tabLeftRightDropRatio = uidl.getFloatAttribute("hDropRatio"); } } /* * (non-Javadoc) * * @see * fi.jasoft.dragdroplayouts.client.ui.interfaces.VDDTabContainer#getTabPosition * (com.google.gwt.user.client.ui.Widget) */ public int getTabPosition(Widget tab) { int idx = -1; for (int i = 0; i < tabBar.getWidgetCount(); i++) { Widget w = tabBar.getWidget(i); if (w.getElement().isOrHasChild(tab.getElement())) { idx = i; break; } } return idx; } /* * (non-Javadoc) * * @see fi.jasoft.dragdroplayouts.client.ui.interfaces.VDDTabContainer# * getTabContentPosition(com.google.gwt.user.client.ui.Widget) */ public int getTabContentPosition(Widget content) { return tabPanel.getWidgetIndex(content); } }