001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.containerpage.client.ui; 029 030import org.opencms.ade.containerpage.client.CmsContainerpageDNDController; 031import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle; 032import org.opencms.ade.containerpage.shared.CmsContainer; 033import org.opencms.gwt.client.dnd.CmsDNDHandler.Orientation; 034import org.opencms.gwt.client.dnd.I_CmsDraggable; 035import org.opencms.gwt.client.dnd.I_CmsDropTarget; 036import org.opencms.gwt.client.ui.CmsHighlightingBorder; 037import org.opencms.gwt.client.util.CmsDebugLog; 038import org.opencms.gwt.client.util.CmsDomUtil; 039import org.opencms.gwt.client.util.CmsDomUtil.Style; 040import org.opencms.gwt.client.util.CmsPositionBean; 041import org.opencms.gwt.shared.CmsGwtConstants; 042import org.opencms.gwt.shared.CmsTemplateContextInfo; 043import org.opencms.util.CmsStringUtil; 044 045import java.util.ArrayList; 046import java.util.HashMap; 047import java.util.Iterator; 048import java.util.List; 049import java.util.Map; 050 051import com.google.gwt.dom.client.Element; 052import com.google.gwt.dom.client.Node; 053import com.google.gwt.dom.client.NodeList; 054import com.google.gwt.dom.client.Style.Display; 055import com.google.gwt.dom.client.Style.Position; 056import com.google.gwt.user.client.ui.ComplexPanel; 057import com.google.gwt.user.client.ui.RootPanel; 058import com.google.gwt.user.client.ui.Widget; 059 060/** 061 * Container page container.<p> 062 * 063 * 064 * 065 * @since 8.0.0 066 */ 067public class CmsContainerPageContainer extends ComplexPanel implements I_CmsDropContainer { 068 069 /** 070 * Helper class for resizing containers in the drag/drop process when an element is dropped into them that is of lower height than the empty container HTML. 071 */ 072 public static class ContainerResizeHelper { 073 074 /** The container. */ 075 private CmsContainerPageContainer m_container; 076 077 /** Minimum height. */ 078 private int m_origHeight; 079 080 /** 081 * Creates a new instance. 082 * 083 * @param container the container 084 */ 085 public ContainerResizeHelper(CmsContainerPageContainer container) { 086 087 m_container = container; 088 089 } 090 091 /** 092 * Initializes the minimum height. 093 */ 094 public void initHeight() { 095 096 int h = m_container.getElement().getOffsetHeight(); 097 m_origHeight = h; 098 } 099 100 /** 101 * Called when the placeholder is shown. 102 * 103 * @param placeholder the placeholder 104 */ 105 public void onShowPlaceholder(Element placeholder) { 106 107 int curHeight = m_container.getElement().getOffsetHeight(); 108 if (curHeight < m_origHeight) { 109 // We want an artificial element to 'blow up' the container to its original size, 110 // but adding a normal element would interfere with drag and drop, so we use the ::after pseudoelement 111 // of the container. We can't just set its style directly, so we modify a stylesheet with 112 // a fixed ID to set the height. 113 m_container.getElement().addClassName(CmsGwtConstants.CLASS_CONTAINER_INFLATED); 114 String styleText = "." + CmsGwtConstants.CLASS_CONTAINER_INFLATED + "::after { "; 115 styleText += "height: " + (m_origHeight - curHeight) + "px;"; 116 styleText += " } "; 117 CmsDomUtil.setStylesheetText("oc-dnd-inflated-container-style", styleText); 118 } 119 } 120 121 /** 122 * Resets the style changes. 123 */ 124 public void reset() { 125 126 m_container.getElement().removeClassName(CmsGwtConstants.CLASS_CONTAINER_INFLATED); 127 } 128 } 129 130 /** 131 * Element position info class.<p> 132 */ 133 protected class ElementPositionInfo { 134 135 /** The DOM element. */ 136 private Element m_element; 137 138 /** The element position bean. */ 139 private CmsPositionBean m_elementPosition; 140 141 /** The float CSS property. */ 142 private String m_float; 143 144 /** Flag indicating the element is positioned absolute. */ 145 private boolean m_isAbsolute; 146 147 /** Flag indicating if the given element is visible. */ 148 private boolean m_isVisible; 149 150 /** 151 * Constructor.<p> 152 * 153 * @param element the DOM element 154 */ 155 public ElementPositionInfo(Element element) { 156 157 m_element = element; 158 String positioning = CmsDomUtil.getCurrentStyle(m_element, Style.position); 159 m_isAbsolute = Position.ABSOLUTE.getCssName().equals(positioning) 160 || Position.FIXED.getCssName().equals(positioning); 161 162 if (!m_isAbsolute) { 163 m_isVisible = !Display.NONE.getCssName().equals(m_element.getStyle().getDisplay()); 164 if (m_isVisible) { 165 m_elementPosition = CmsPositionBean.getBoundingClientRect(element); 166 m_float = CmsDomUtil.getCurrentStyle(m_element, Style.floatCss); 167 } 168 } 169 } 170 171 /** 172 * Returns the DOM element.<p> 173 * 174 * @return the DOM element 175 */ 176 public Element getElement() { 177 178 return m_element; 179 } 180 181 /** 182 * Returns the element position bean.<p> 183 * 184 * @return the element position bean 185 */ 186 public CmsPositionBean getElementPosition() { 187 188 return m_elementPosition; 189 } 190 191 /** 192 * Returns the x distance of the cursor to the element left.<p> 193 * 194 * @param x the cursor x position 195 * @param documentScrollLeft the document scroll left position 196 * 197 * @return the y distance of the cursor to the element top 198 */ 199 public int getRelativeLeft(int x, int documentScrollLeft) { 200 201 return (x + documentScrollLeft) - m_elementPosition.getLeft(); 202 } 203 204 /** 205 * Returns the y distance of the cursor to the element top.<p> 206 * 207 * @param y the cursor y position 208 * @param documentScrollTop the document scroll top position 209 * 210 * @return the y distance of the cursor to the element top 211 */ 212 public int getRelativeTop(int y, int documentScrollTop) { 213 214 return (y + documentScrollTop) - m_elementPosition.getTop(); 215 } 216 217 /** 218 * Returns if the element is positioned absolute.<p> 219 * 220 * @return <code>true</code> if the element is positioned absolute 221 */ 222 public boolean isAbsolute() { 223 224 return m_isAbsolute; 225 } 226 227 /** 228 * Returns if the element is floated.<p> 229 * 230 * @return <code>true</code> if the element is floated 231 */ 232 public boolean isFloating() { 233 234 return isFloatLeft() || isFloatRight(); 235 } 236 237 /** 238 * Returns if the element is floated to the left.<p> 239 * 240 * @return <code>true</code> if the element is floated to the left 241 */ 242 public boolean isFloatLeft() { 243 244 return "left".equals(m_float); 245 } 246 247 /** 248 * Returns if the element is floated to the right.<p> 249 * 250 * @return <code>true</code> if the element is floated to the right 251 */ 252 public boolean isFloatRight() { 253 254 return "right".equals(m_float); 255 } 256 257 /** 258 * Returns if the given element is visible.<p> 259 * 260 * @return <code>true</code> if the given element is visible 261 */ 262 public boolean isVisible() { 263 264 return m_isVisible; 265 } 266 267 } 268 269 /** Name of a special property for the container id. */ 270 public static final String PROP_CONTAINER_MARKER = "opencmsContainerId"; 271 272 /** Static variable for storing the container layout change helper for the current drag/drop process. */ 273 private static ContainerResizeHelper RESIZE_HELPER; 274 275 /** The container data. */ 276 private CmsContainer m_containerData; 277 278 /** The container level. */ 279 private int m_containerLevel; 280 281 /** The list of nested sub containers that are also valid drop targets during the current drag and drop. */ 282 private List<I_CmsDropTarget> m_dnDChildren; 283 284 /** The element position info cache. */ 285 private List<ElementPositionInfo> m_elementPositions; 286 287 /** The element to display in case the container is empty. */ 288 private Element m_emptyContainerElement; 289 290 /** Highlighting border for this container. */ 291 private CmsHighlightingBorder m_highlighting; 292 293 /** The overflowing element. */ 294 private Widget m_overflowingElement; 295 296 /** The cached highlighting position. */ 297 private CmsPositionBean m_ownPosition; 298 299 /** The drag and drop placeholder. */ 300 private Element m_placeholder; 301 302 /** The drag and drop placeholder position index. */ 303 private int m_placeholderIndex = -1; 304 305 /** Flag indicating the current place holder visibility. */ 306 private boolean m_placeholderVisible; 307 308 /** Flag indicating the element positions need to be re-evaluated. */ 309 private boolean m_requiresPositionUpdate = true; 310 311 /** 312 * Constructor.<p> 313 * 314 * @param containerData the container data 315 * @param element the container element 316 */ 317 public CmsContainerPageContainer(CmsContainer containerData, Element element) { 318 319 setElement(element); 320 321 if (!containerData.isSubContainer()) { 322 RootPanel.detachOnWindowClose(this); 323 } 324 m_containerData = containerData; 325 element.setPropertyString(PROP_CONTAINER_MARKER, containerData.getName()); 326 if (m_containerData.isEditable()) { 327 addStyleName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragTarget()); 328 } 329 onAttach(); 330 } 331 332 /** 333 * Clears the static layout change object, resetting it if it's not null. 334 */ 335 public static void clearResizeHelper() { 336 337 if (RESIZE_HELPER != null) { 338 try { 339 RESIZE_HELPER.reset(); 340 } finally { 341 RESIZE_HELPER = null; 342 } 343 } 344 } 345 346 /** 347 * Measures the height of the container's element. 348 * 349 * This sets the overflow-y style property to auto to prevent margin collapsing. 350 * 351 * @param elem the element 352 * @return the height 353 */ 354 public static int measureHeight(Element elem) { 355 356 Map<String, String> props = new HashMap<>(); 357 props.put("overflowY", "auto"); 358 Map<String, String> old = CmsDomUtil.updateStyle(elem.getStyle(), props); 359 int result = elem.getOffsetHeight(); 360 CmsDomUtil.updateStyle(elem.getStyle(), old); 361 return result; 362 363 } 364 365 /** 366 * Creates a new layout helper for resizing containers.<p> 367 * 368 * The previously created layout changes object (if any) will be reset. 369 * 370 * @param container the container 371 * @return the new layout helper 372 */ 373 public static ContainerResizeHelper newResizeHelper(CmsContainerPageContainer container) { 374 375 clearResizeHelper(); 376 RESIZE_HELPER = new ContainerResizeHelper(container); 377 return RESIZE_HELPER; 378 } 379 380 /** 381 * @see com.google.gwt.user.client.ui.Panel#add(com.google.gwt.user.client.ui.Widget) 382 */ 383 @Override 384 public void add(Widget w) { 385 386 add(w, (Element)getElement()); 387 } 388 389 /** 390 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#addDndChild(org.opencms.gwt.client.dnd.I_CmsDropTarget) 391 */ 392 public void addDndChild(I_CmsDropTarget child) { 393 394 if (m_dnDChildren == null) { 395 m_dnDChildren = new ArrayList<I_CmsDropTarget>(); 396 } 397 m_dnDChildren.add(child); 398 } 399 400 /** 401 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#adoptElement(org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel) 402 */ 403 public void adoptElement(CmsContainerPageElementPanel containerElement) { 404 405 assert getElement().equals(containerElement.getElement().getParentElement()); 406 getChildren().add(containerElement); 407 adopt(containerElement); 408 } 409 410 /** 411 * Check if the empty container content should be displayed or removed.<p> 412 */ 413 public void checkEmptyContainers() { 414 415 if (getWidgetCount() == 0) { 416 if (m_emptyContainerElement != null) { 417 m_emptyContainerElement.getStyle().clearDisplay(); 418 } else if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_containerData.getEmptyContainerContent())) { 419 // add empty container element 420 try { 421 m_emptyContainerElement = CmsDomUtil.createElement(m_containerData.getEmptyContainerContent()); 422 getElement().appendChild(m_emptyContainerElement); 423 } catch (Exception e) { 424 CmsDebugLog.getInstance().printLine(e.getMessage()); 425 } 426 } 427 } else if (m_emptyContainerElement != null) { 428 m_emptyContainerElement.removeFromParent(); 429 m_emptyContainerElement = null; 430 } 431 } 432 433 /** 434 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#checkMaxElementsOnEnter() 435 */ 436 public void checkMaxElementsOnEnter() { 437 438 int count = getWidgetCount(); 439 if (count >= m_containerData.getMaxElements()) { 440 Widget overflowElement = null; 441 int index = 0; 442 for (Widget widget : this) { 443 boolean isDummy = widget.getStyleName().contains(CmsTemplateContextInfo.DUMMY_ELEMENT_MARKER); 444 if (!isDummy) { 445 index++; 446 if (index >= m_containerData.getMaxElements()) { 447 if (overflowElement == null) { 448 overflowElement = widget; 449 } 450 } 451 } 452 } 453 if (overflowElement != null) { 454 m_overflowingElement = overflowElement; 455 m_overflowingElement.removeFromParent(); 456 } 457 } 458 if (count == 0) { 459 if (m_emptyContainerElement != null) { 460 m_emptyContainerElement.getStyle().setDisplay(Display.NONE); 461 } 462 } 463 } 464 465 /** 466 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#checkMaxElementsOnLeave() 467 */ 468 public void checkMaxElementsOnLeave() { 469 470 if (m_overflowingElement != null) { 471 add(m_overflowingElement); 472 } 473 if (m_emptyContainerElement != null) { 474 m_emptyContainerElement.getStyle().clearDisplay(); 475 } 476 } 477 478 /** 479 * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#checkPosition(int, int, Orientation) 480 */ 481 public boolean checkPosition(int x, int y, Orientation orientation) { 482 483 if (m_ownPosition != null) { 484 // ignore orientation 485 int scrollTop = getElement().getOwnerDocument().getScrollTop(); 486 // use cached position 487 int relativeTop = (y + scrollTop) - m_ownPosition.getTop(); 488 if ((relativeTop > 0) && (m_ownPosition.getHeight() > relativeTop)) { 489 // cursor is inside the height of the element, check horizontal position 490 int scrollLeft = getElement().getOwnerDocument().getScrollLeft(); 491 int relativeLeft = (x + scrollLeft) - m_ownPosition.getLeft(); 492 return (relativeLeft > 0) && (m_ownPosition.getWidth() > relativeLeft); 493 } 494 } 495 return false; 496 } 497 498 /** 499 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#clearDnDChildren() 500 */ 501 public void clearDnDChildren() { 502 503 if (m_dnDChildren != null) { 504 m_dnDChildren.clear(); 505 } 506 } 507 508 /** 509 * Returns all contained drag elements.<p> 510 * 511 * @return the drag elements 512 */ 513 public List<CmsContainerPageElementPanel> getAllDragElements() { 514 515 List<CmsContainerPageElementPanel> elements = new ArrayList<CmsContainerPageElementPanel>(); 516 Iterator<Widget> it = iterator(); 517 while (it.hasNext()) { 518 Widget w = it.next(); 519 if (w instanceof CmsContainerPageElementPanel) { 520 elements.add((CmsContainerPageElementPanel)w); 521 } else { 522 if (CmsDomUtil.hasClass( 523 org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle.INSTANCE.containerpageCss().groupcontainerPlaceholder(), 524 w.getElement())) { 525 CmsDebugLog.getInstance().printLine("Ignoring group container placeholder."); 526 } else { 527 CmsDebugLog.getInstance().printLine( 528 "WARNING: " + w.toString() + " is no instance of CmsDragContainerElement"); 529 } 530 } 531 } 532 return elements; 533 } 534 535 /** 536 * Returns the configured width for this container.<p> 537 * 538 * @return the configured width 539 */ 540 public int getConfiguredWidth() { 541 542 return m_containerData.getWidth(); 543 } 544 545 /** 546 * Returns the container id.<p> 547 * 548 * @return the container id 549 */ 550 public String getContainerId() { 551 552 return m_containerData.getName(); 553 } 554 555 /** 556 * Returns the container level.<p> 557 * 558 * @return the container level 559 */ 560 public int getContainerLevel() { 561 562 return m_containerLevel; 563 } 564 565 /** 566 * Returns the container type.<p> 567 * 568 * @return the container type 569 */ 570 public String getContainerType() { 571 572 return m_containerData.getType(); 573 } 574 575 /** 576 * In case of a former copy model, and a max elements setting of one, the id of the overflowing element is returned.<p> 577 * 578 * @return the overflowing element id or <code>null</code> 579 */ 580 public String getCopyModelReplaceId() { 581 582 String result = null; 583 if ((m_containerData.getMaxElements() == 1) 584 && (m_overflowingElement != null) 585 && (m_overflowingElement instanceof CmsContainerPageElementPanel) 586 && (getFormerModelGroupParent() != null)) { 587 result = ((CmsContainerPageElementPanel)m_overflowingElement).getId(); 588 } 589 return result; 590 } 591 592 /** 593 * @see org.opencms.gwt.client.dnd.I_CmsNestedDropTarget#getDnDChildren() 594 */ 595 public List<I_CmsDropTarget> getDnDChildren() { 596 597 return m_dnDChildren; 598 } 599 600 /** 601 * Returns whether this container has a model group parent.<p> 602 * 603 * @return <code>true</code> if this container has a model group parent 604 */ 605 public Element getFormerModelGroupParent() { 606 607 Element result = null; 608 Element parent = getElement().getParentElement(); 609 while (parent != null) { 610 if (parent.getPropertyBoolean(CmsContainerPageElementPanel.PROP_WAS_MODEL_GROUP)) { 611 result = parent; 612 break; 613 } 614 parent = parent.getParentElement(); 615 } 616 return result; 617 } 618 619 /** 620 * Returns the parent container id.<p> 621 * 622 * @return the container parent id 623 */ 624 public String getParentContainerId() { 625 626 return m_containerData.getParentContainerName(); 627 } 628 629 /** 630 * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#getPlaceholderIndex() 631 */ 632 public int getPlaceholderIndex() { 633 634 return m_placeholderIndex; 635 } 636 637 /** 638 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#getPositionInfo() 639 */ 640 public CmsPositionBean getPositionInfo() { 641 642 return m_ownPosition; 643 } 644 645 /** 646 * Returns the settings presets.<p> 647 * 648 * @return the presets 649 */ 650 public Map<String, String> getSettingPresets() { 651 652 return m_containerData.getSettingPresets(); 653 } 654 655 /** 656 * @see org.opencms.gwt.client.dnd.I_CmsNestedDropTarget#hasDnDChildren() 657 */ 658 public boolean hasDnDChildren() { 659 660 return (m_dnDChildren != null) && !m_dnDChildren.isEmpty(); 661 } 662 663 /** 664 * Returns whether this container has a model group parent.<p> 665 * 666 * @return <code>true</code> if this container has a model group parent 667 */ 668 public boolean hasModelGroupParent() { 669 670 boolean result = false; 671 Element parent = getElement(); 672 while (parent != null) { 673 if (parent.getPropertyBoolean(CmsContainerPageElementPanel.PROP_IS_MODEL_GROUP)) { 674 result = true; 675 break; 676 } 677 parent = parent.getParentElement(); 678 } 679 return result; 680 } 681 682 /** 683 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#hideEditableListButtons() 684 */ 685 public void hideEditableListButtons() { 686 687 Iterator<Widget> it = iterator(); 688 while (it.hasNext()) { 689 Widget child = it.next(); 690 if (child instanceof CmsContainerPageElementPanel) { 691 ((CmsContainerPageElementPanel)child).hideEditableListButtons(); 692 } 693 } 694 } 695 696 /** 697 * Puts a highlighting border around the container content.<p> 698 */ 699 public void highlightContainer() { 700 701 highlightContainer(CmsPositionBean.getBoundingClientRect(getElement())); 702 } 703 704 /** 705 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#highlightContainer(org.opencms.gwt.client.util.CmsPositionBean) 706 */ 707 public void highlightContainer(CmsPositionBean positionInfo) { 708 709 // remove any remaining highlighting 710 if (m_highlighting != null) { 711 m_highlighting.removeFromParent(); 712 } 713 // cache the position info, to be used during drag and drop 714 m_ownPosition = positionInfo; 715 m_highlighting = new CmsHighlightingBorder( 716 m_ownPosition, 717 CmsHighlightingBorder.BorderColor.red, 718 CmsContainerpageDNDController.HIGHLIGHTING_OFFSET); 719 RootPanel.get().add(m_highlighting); 720 } 721 722 /** 723 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#insert(com.google.gwt.user.client.ui.Widget, int) 724 */ 725 public void insert(Widget w, int beforeIndex) { 726 727 // in case an option bar as a direct child is present it may disturb the insert order 728 // it needs to be detached first 729 Element optionBar = null; 730 NodeList<Node> children = getElement().getChildNodes(); 731 for (int i = 0; i < children.getLength(); i++) { 732 Node child = children.getItem(i); 733 if ((child.getNodeType() == Node.ELEMENT_NODE) 734 && ((Element)child).hasClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().optionBar())) { 735 optionBar = (Element)child; 736 optionBar.removeFromParent(); 737 break; 738 } 739 740 } 741 742 insert(w, (Element)getElement(), beforeIndex, true); 743 if (optionBar != null) { 744 getElement().insertFirst(optionBar); 745 } 746 } 747 748 /** 749 * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#insertPlaceholder(com.google.gwt.dom.client.Element, int, int, Orientation) 750 */ 751 public void insertPlaceholder(Element placeholder, int x, int y, Orientation orientation) { 752 753 m_placeholder = placeholder; 754 m_placeholderVisible = false; 755 m_placeholder.getStyle().setDisplay(Display.NONE); 756 m_requiresPositionUpdate = true; 757 repositionPlaceholder(x, y, orientation); 758 } 759 760 /** 761 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#isDetailOnly() 762 */ 763 public boolean isDetailOnly() { 764 765 return m_containerData.isDetailOnly(); 766 } 767 768 /** 769 * Returns true if this is a detail view container, being actually used for detail content.<p> 770 * 771 * @return true if this is a detail view container 772 */ 773 public boolean isDetailView() { 774 775 return m_containerData.isDetailView(); 776 } 777 778 /** 779 * Checks if this is a detail view container. 780 * 781 * @return true if this is a detail view container 782 */ 783 public boolean isDetailViewContainer() { 784 785 return m_containerData.isDetailViewContainer(); 786 } 787 788 /** 789 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#isEditable() 790 */ 791 public boolean isEditable() { 792 793 return m_containerData.isEditable(); 794 } 795 796 /** 797 * Checks if the container is showing the empty container element. 798 * 799 * @return true if the empty container element is shown in the container 800 */ 801 public boolean isShowingEmptyContainerElement() { 802 803 return (m_emptyContainerElement != null) && (m_emptyContainerElement.getParentElement() == getElement()); 804 } 805 806 /** 807 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#onConsumeChildren(java.util.List) 808 */ 809 public void onConsumeChildren(List<CmsContainerPageElementPanel> children) { 810 811 // nothing to do 812 } 813 814 /** 815 * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#onDrop(org.opencms.gwt.client.dnd.I_CmsDraggable) 816 */ 817 public void onDrop(I_CmsDraggable draggable) { 818 819 m_overflowingElement = null; 820 } 821 822 /** 823 * Refreshes position and dimension of the highlighting border. Call when anything changed during the drag process.<p> 824 */ 825 public void refreshHighlighting() { 826 827 if (m_highlighting != null) { 828 refreshHighlighting(CmsPositionBean.getBoundingClientRect(getElement())); 829 } 830 } 831 832 /** 833 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#refreshHighlighting(org.opencms.gwt.client.util.CmsPositionBean) 834 */ 835 public void refreshHighlighting(CmsPositionBean positionInfo) { 836 837 // cache the position info, to be used during drag and drop 838 m_ownPosition = positionInfo; 839 if (m_highlighting != null) { 840 m_highlighting.setPosition(m_ownPosition); 841 } 842 } 843 844 /** 845 * Removes the highlighting border.<p> 846 */ 847 public void removeHighlighting() { 848 849 if (m_highlighting != null) { 850 m_highlighting.removeFromParent(); 851 m_highlighting = null; 852 } 853 } 854 855 /** 856 * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#removePlaceholder() 857 */ 858 public void removePlaceholder() { 859 860 if (m_placeholder != null) { 861 m_placeholder.removeFromParent(); 862 m_placeholder = null; 863 } 864 m_placeholderIndex = -1; 865 m_requiresPositionUpdate = true; 866 867 // check if the empty container content should be displayed or removed 868 checkEmptyContainers(); 869 } 870 871 /** 872 * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#repositionPlaceholder(int, int, Orientation) 873 */ 874 public void repositionPlaceholder(int x, int y, Orientation orientation) { 875 876 if (m_requiresPositionUpdate) { 877 updatePositionsList(); 878 } 879 int newPlaceholderIndex = internalRepositionPlaceholder(x, y); 880 m_requiresPositionUpdate = newPlaceholderIndex != m_placeholderIndex; 881 m_placeholderIndex = newPlaceholderIndex; 882 } 883 884 /** 885 * Sets the container level.<p> 886 * 887 * @param level the container level 888 */ 889 public void setContainerLevel(int level) { 890 891 m_containerLevel = level; 892 } 893 894 /** 895 * Sets the empty container element.<p> 896 * 897 * @param emptyContainerElement the empty container element 898 */ 899 public void setEmptyContainerElement(Element emptyContainerElement) { 900 901 m_emptyContainerElement = emptyContainerElement; 902 } 903 904 /** 905 * Measures the height of the container and sets its min-height to that value. 906 * 907 * @return a runnable used to undo the style changes 908 */ 909 public Runnable setMinHeightToCurrentHeight() { 910 911 int h1 = measureHeight(getElement()); 912 Map<String, String> props = new HashMap<>(); 913 props.put("minHeight", h1 + "px"); 914 props.put( 915 "background", 916 "repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(240, 0, 242, 1) 10px, rgba(240, 0, 242, 1) 20px)"); 917 com.google.gwt.dom.client.Style style = getElement().getStyle(); 918 final Map<String, String> oldVals = CmsDomUtil.updateStyle(style, props); 919 return () -> { 920 CmsDomUtil.updateStyle(style, oldVals); 921 }; 922 } 923 924 /** 925 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#setPlaceholderVisibility(boolean) 926 */ 927 public void setPlaceholderVisibility(boolean visible) { 928 929 if (m_placeholderVisible != visible) { 930 m_placeholderVisible = visible; 931 m_requiresPositionUpdate = true; 932 if (m_placeholderVisible) { 933 m_placeholder.getStyle().clearDisplay(); 934 if (!m_placeholder.hasClassName(CmsGwtConstants.CLASS_PLACEHOLDER_TOO_BIG)) { 935 int height = m_placeholder.getOffsetHeight(); 936 if (height > CmsGwtConstants.MAX_PLACEHOLDER_HEIGHT) { 937 m_placeholder.addClassName(CmsGwtConstants.CLASS_PLACEHOLDER_TOO_BIG); 938 } 939 } 940 if (RESIZE_HELPER != null) { 941 RESIZE_HELPER.onShowPlaceholder(m_placeholder); 942 } 943 } else { 944 m_placeholder.getStyle().setDisplay(Display.NONE); 945 } 946 } 947 } 948 949 /** 950 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#showEditableListButtons() 951 */ 952 public void showEditableListButtons() { 953 954 for (Widget child : this) { 955 if (child instanceof CmsContainerPageElementPanel) { 956 ((CmsContainerPageElementPanel)child).showEditableListButtons(); 957 } 958 } 959 } 960 961 /** 962 * Updates the option bar positions of the child elements.<p> 963 */ 964 public void updateOptionBars() { 965 966 for (Widget widget : this) { 967 if (widget instanceof CmsContainerPageElementPanel) { 968 ((CmsContainerPageElementPanel)widget).updateOptionBarPosition(); 969 } 970 } 971 } 972 973 /** 974 * @see org.opencms.ade.containerpage.client.ui.I_CmsDropContainer#updatePositionInfo() 975 */ 976 public void updatePositionInfo() { 977 978 m_ownPosition = CmsPositionBean.getBoundingClientRect(getElement()); 979 } 980 981 /** 982 * Repositions the drag and drop placeholder.<p> 983 * 984 * @param x the x cursor position 985 * @param y the y cursor position 986 * 987 * @return the placeholder position index 988 */ 989 private int internalRepositionPlaceholder(int x, int y) { 990 991 int indexCorrection = 0; 992 int previousTop = 0; 993 int documentScrollTop = getElement().getOwnerDocument().getScrollTop(); 994 int documentScrollLeft = getElement().getOwnerDocument().getScrollLeft(); 995 for (int index = 0; index < m_elementPositions.size(); index++) { 996 ElementPositionInfo info = m_elementPositions.get(index); 997 if (info.getElement() == m_placeholder) { 998 indexCorrection = 1; 999 } 1000 if (info.isAbsolute() || !info.isVisible()) { 1001 continue; 1002 } 1003 1004 int top = info.getRelativeTop(y, documentScrollTop); 1005 1006 if ((top <= 0) || (top >= info.getElementPosition().getHeight())) { 1007 previousTop = top; 1008 continue; 1009 } 1010 int left = info.getRelativeLeft(x, documentScrollLeft); 1011 if ((left <= 0) || (left >= info.getElementPosition().getWidth())) { 1012 previousTop = top; 1013 continue; 1014 } 1015 boolean floatSort = info.isFloating() && (top != 0) && (top == previousTop); 1016 previousTop = top; 1017 if (info.getElement() != m_placeholder) { 1018 if (floatSort) { 1019 boolean insertBefore = false; 1020 if (left < (info.getElementPosition().getWidth() / 2)) { 1021 if (info.isFloatLeft()) { 1022 insertBefore = true; 1023 } 1024 } else if (info.isFloatRight()) { 1025 insertBefore = true; 1026 } 1027 if (insertBefore) { 1028 getElement().insertBefore(m_placeholder, info.getElement()); 1029 return index - indexCorrection; 1030 } else { 1031 getElement().insertAfter(m_placeholder, info.getElement()); 1032 return (index + 1) - indexCorrection; 1033 } 1034 } else { 1035 if (top < (info.getElementPosition().getHeight() / 2)) { 1036 getElement().insertBefore(m_placeholder, info.getElement()); 1037 return index - indexCorrection; 1038 } else { 1039 getElement().insertAfter(m_placeholder, info.getElement()); 1040 return (index + 1) - indexCorrection; 1041 } 1042 } 1043 } else { 1044 return index; 1045 } 1046 } 1047 1048 // not over any child position 1049 if ((m_placeholderIndex >= 0) && (m_placeholder.getParentElement() == getElement())) { 1050 // element is already attached to this parent and no new position available 1051 // don't do anything 1052 return m_placeholderIndex; 1053 } 1054 int top = CmsDomUtil.getRelativeY(y, getElement()); 1055 int offsetHeight = getElement().getOffsetHeight(); 1056 if ((top >= (offsetHeight / 2))) { 1057 // over top half, insert as first child 1058 getElement().insertFirst(m_placeholder); 1059 return 0; 1060 } 1061 // over bottom half, insert as last child 1062 getElement().appendChild(m_placeholder); 1063 return getElement().getChildCount() - 1; 1064 } 1065 1066 /** 1067 * Updates the element position cache during drag and drop.<p> 1068 */ 1069 private void updatePositionsList() { 1070 1071 CmsDebugLog.getInstance().printLine("Updating positions"); 1072 if (m_elementPositions != null) { 1073 m_elementPositions.clear(); 1074 } else { 1075 m_elementPositions = new ArrayList<ElementPositionInfo>(); 1076 } 1077 for (int index = 0; index < getElement().getChildCount(); index++) { 1078 Node node = getElement().getChild(index); 1079 // in some cases the container element may have an option bar as a direct child, ignore it 1080 if ((node.getNodeType() != Node.ELEMENT_NODE) 1081 || ((Element)node).hasClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().optionBar())) { 1082 continue; 1083 } 1084 m_elementPositions.add(new ElementPositionInfo((Element)node)); 1085 } 1086 m_requiresPositionUpdate = false; 1087 m_ownPosition = CmsPositionBean.getBoundingClientRect(getElement()); 1088 } 1089}