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.gwt.client.dnd; 029 030import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 031import org.opencms.gwt.client.util.A_CmsAnimation; 032import org.opencms.gwt.client.util.CmsDebugLog; 033import org.opencms.gwt.client.util.CmsDomUtil; 034import org.opencms.gwt.client.util.CmsDomUtil.Style; 035import org.opencms.gwt.client.util.CmsMoveAnimation; 036import org.opencms.gwt.client.util.CmsPositionBean; 037 038import java.util.ArrayList; 039import java.util.List; 040 041import com.google.gwt.animation.client.Animation; 042import com.google.gwt.dom.client.Document; 043import com.google.gwt.dom.client.Element; 044import com.google.gwt.dom.client.NativeEvent; 045import com.google.gwt.dom.client.Style.Overflow; 046import com.google.gwt.dom.client.Style.Unit; 047import com.google.gwt.event.dom.client.MouseDownEvent; 048import com.google.gwt.event.dom.client.MouseDownHandler; 049import com.google.gwt.event.shared.HandlerRegistration; 050import com.google.gwt.user.client.Command; 051import com.google.gwt.user.client.DOM; 052import com.google.gwt.user.client.Event; 053import com.google.gwt.user.client.Event.NativePreviewEvent; 054import com.google.gwt.user.client.Event.NativePreviewHandler; 055import com.google.gwt.user.client.Timer; 056import com.google.gwt.user.client.Window; 057import com.google.gwt.user.client.ui.RootPanel; 058 059/** 060 * Drag and drop handler.<p> 061 * 062 * @since 8.0.0 063 */ 064public class CmsDNDHandler implements MouseDownHandler { 065 066 /** The animation types. */ 067 public enum AnimationType { 068 /** Move animation. */ 069 MOVE, 070 /** No animation. */ 071 NONE, 072 /** Fade animation. */ 073 SPECIAL 074 } 075 076 /** The allowed drag and drop orientation. */ 077 public enum Orientation { 078 /** Drag and drop in all directions. */ 079 ALL, 080 /** Only horizontal drag and drop, the client-y position will be ignored. */ 081 HORIZONTAL, 082 /** Only vertical drag and drop, the client-x position will be ignored. */ 083 VERTICAL 084 } 085 086 /** 087 * Timer to schedule automated scrolling.<p> 088 */ 089 protected class CmsScrollTimer extends Timer { 090 091 /** The current scroll direction. */ 092 private Direction m_direction; 093 094 /** Flag indicating if the scroll parent is the body element. */ 095 private boolean m_isBody; 096 097 /** The element that should scrolled. */ 098 private Element m_scrollParent; 099 100 /** The scroll speed. */ 101 private int m_scrollSpeed; 102 103 /** 104 * Constructor.<p> 105 * 106 * @param scrollParent the element that should scrolled 107 * @param scrollSpeed the scroll speed 108 * @param direction the scroll direction 109 */ 110 public CmsScrollTimer(Element scrollParent, int scrollSpeed, Direction direction) { 111 112 m_scrollParent = scrollParent; 113 m_scrollSpeed = scrollSpeed; 114 m_isBody = m_scrollParent.getTagName().equalsIgnoreCase(CmsDomUtil.Tag.body.name()); 115 m_direction = direction; 116 } 117 118 /** 119 * @see com.google.gwt.user.client.Timer#run() 120 */ 121 @Override 122 public void run() { 123 124 int top, left; 125 if (m_isBody) { 126 top = Window.getScrollTop(); 127 left = Window.getScrollLeft(); 128 } else { 129 top = m_scrollParent.getScrollTop(); 130 left = m_scrollParent.getScrollLeft(); 131 } 132 Element element = getDragHelper(); 133 134 boolean abort = false; 135 switch (m_direction) { 136 case down: 137 top += m_scrollSpeed; 138 element.getStyle().setTop( 139 CmsDomUtil.getCurrentStyleInt(element, Style.top) + m_scrollSpeed, 140 Unit.PX); 141 break; 142 case up: 143 if (top <= m_scrollSpeed) { 144 abort = true; 145 top = 0; 146 element.getStyle().setTop(CmsDomUtil.getCurrentStyleInt(element, Style.top) - top, Unit.PX); 147 break; 148 } 149 top -= m_scrollSpeed; 150 element.getStyle().setTop( 151 CmsDomUtil.getCurrentStyleInt(element, Style.top) - m_scrollSpeed, 152 Unit.PX); 153 break; 154 case left: 155 if (left <= m_scrollSpeed) { 156 abort = true; 157 element.getStyle().setLeft(CmsDomUtil.getCurrentStyleInt(element, Style.left) - left, Unit.PX); 158 left = 0; 159 break; 160 } 161 left -= m_scrollSpeed; 162 element.getStyle().setLeft( 163 CmsDomUtil.getCurrentStyleInt(element, Style.left) - m_scrollSpeed, 164 Unit.PX); 165 break; 166 case right: 167 left += m_scrollSpeed; 168 element.getStyle().setLeft( 169 CmsDomUtil.getCurrentStyleInt(element, Style.left) + m_scrollSpeed, 170 Unit.PX); 171 break; 172 default: 173 break; 174 175 } 176 177 if (m_isBody) { 178 Window.scrollTo(left, top); 179 } else { 180 m_scrollParent.setScrollLeft(left); 181 m_scrollParent.setScrollTop(top); 182 } 183 if (abort) { 184 clearScrollTimer(); 185 } 186 } 187 } 188 189 /** Scroll direction enumeration. */ 190 protected enum Direction { 191 /** Scroll direction. */ 192 down, 193 194 /** Scroll direction. */ 195 left, 196 197 /** Scroll direction. */ 198 right, 199 200 /** Scroll direction. */ 201 up 202 } 203 204 /** 205 * Drag and drop event preview handler.<p> 206 * 207 * To be used while dragging.<p> 208 */ 209 protected class DNDEventPreviewHandler implements NativePreviewHandler { 210 211 /** 212 * @see com.google.gwt.user.client.Event.NativePreviewHandler#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent) 213 */ 214 public void onPreviewNativeEvent(NativePreviewEvent event) { 215 216 if (!isDragging()) { 217 // this should never happen, as the preview handler should be removed after the dragging stopped 218 CmsDebugLog.getInstance().printLine("Preview handler still registered, even though dragging stopped."); 219 stopDragging(); 220 return; 221 } 222 Event nativeEvent = Event.as(event.getNativeEvent()); 223 switch (DOM.eventGetType(nativeEvent)) { 224 case Event.ONMOUSEMOVE: 225 // dragging 226 onMove(nativeEvent); 227 break; 228 case Event.ONMOUSEUP: 229 onUp(nativeEvent); 230 break; 231 case Event.ONKEYDOWN: 232 if (nativeEvent.getKeyCode() == 27) { 233 cancel(); 234 } 235 break; 236 case Event.ONMOUSEWHEEL: 237 onMouseWheelScroll(nativeEvent); 238 break; 239 default: 240 // do nothing 241 } 242 event.cancel(); 243 nativeEvent.preventDefault(); 244 nativeEvent.stopPropagation(); 245 } 246 247 } 248 249 /** 250 * Fancy animation fading out the helper and shrinking away the original element.<p> 251 */ 252 class SpecialAnimation extends A_CmsAnimation { 253 254 /** The helper element. */ 255 private Element m_helper; 256 257 /** If the original should be hidden. */ 258 private boolean m_hideOriginal; 259 260 /** The original element. */ 261 private Element m_original; 262 263 /** The original element height. */ 264 private int m_originalHeight; 265 266 /** The original element opacity. */ 267 private double m_originalOpacity; 268 269 /** The original element width. */ 270 private int m_originalWidth; 271 272 /** The optional overlay. */ 273 private Element m_overlay; 274 275 /** 276 * Constructor.<p> 277 * 278 * @param helper the helper element 279 * @param original the original element 280 * @param overlay the optional overlay to fade away 281 * @param callback the on complete callback 282 * @param hideOriginal if the original should be hidden 283 */ 284 public SpecialAnimation( 285 Element helper, 286 Element original, 287 Element overlay, 288 Command callback, 289 boolean hideOriginal) { 290 291 super(callback); 292 m_helper = helper; 293 m_original = original; 294 m_hideOriginal = hideOriginal; 295 m_overlay = overlay; 296 } 297 298 /** 299 * @see com.google.gwt.animation.client.Animation#run(int) 300 */ 301 @Override 302 public void run(int duration) { 303 304 m_original.getStyle().setOverflow(Overflow.HIDDEN); 305 306 if (m_hideOriginal) { 307 m_original.addClassName(I_CmsLayoutBundle.INSTANCE.generalCss().clearFix()); 308 m_originalHeight = CmsDomUtil.getCurrentStyleInt(m_original, Style.height); 309 m_originalWidth = CmsDomUtil.getCurrentStyleInt(m_original, Style.width); 310 } else { 311 m_originalOpacity = CmsDomUtil.getCurrentStyleFloat(m_original, Style.opacity); 312 } 313 super.run(duration); 314 } 315 316 /** 317 * @see org.opencms.gwt.client.util.A_CmsAnimation#onComplete() 318 */ 319 @Override 320 protected void onComplete() { 321 322 super.onComplete(); 323 m_original.getStyle().clearHeight(); 324 m_original.getStyle().clearWidth(); 325 m_original.getStyle().clearOverflow(); 326 m_original.removeClassName(I_CmsLayoutBundle.INSTANCE.generalCss().clearFix()); 327 } 328 329 /** 330 * @see com.google.gwt.animation.client.Animation#onUpdate(double) 331 */ 332 @Override 333 protected void onUpdate(double progress) { 334 335 m_helper.getStyle().setOpacity(-progress + 1); 336 if (m_overlay != null) { 337 m_overlay.getStyle().setOpacity(-progress + 1); 338 } 339 340 if (m_hideOriginal) { 341 m_original.getStyle().setHeight(m_originalHeight - (m_originalHeight * progress), Unit.PX); 342 m_original.getStyle().setWidth(m_originalWidth - (m_originalWidth * progress), Unit.PX); 343 } else { 344 m_original.getStyle().setOpacity(m_originalOpacity + ((1 - m_originalOpacity) * progress)); 345 } 346 347 } 348 } 349 350 /** Animation enabled flag. */ 351 private AnimationType m_animationType = AnimationType.MOVE; 352 353 /** The mouse x position of the current mouse event. */ 354 private int m_clientX; 355 356 /** The mouse y position of the current mouse event. */ 357 private int m_clientY; 358 359 /** The Drag and drop controller. */ 360 private I_CmsDNDController m_controller; 361 362 /** The current animation. */ 363 private Animation m_currentAnimation; 364 365 /** The current drop target. */ 366 private I_CmsDropTarget m_currentTarget; 367 368 /** The x cursor offset to the dragged element. */ 369 private int m_cursorOffsetX; 370 371 /** The y cursor offset to the dragged element. */ 372 private int m_cursorOffsetY; 373 374 /** The draggable. */ 375 private I_CmsDraggable m_draggable; 376 377 /** The dragging flag. */ 378 private boolean m_dragging; 379 380 /** The drag helper. */ 381 private Element m_dragHelper; 382 383 /** The drag and drop orientation. Default is <code>ALL</code>. */ 384 private Orientation m_orientation = Orientation.ALL; 385 386 /** The placeholder. */ 387 private Element m_placeholder; 388 389 /** The event preview handler. */ 390 private DNDEventPreviewHandler m_previewHandler; 391 392 /** The preview handler registration. */ 393 private HandlerRegistration m_previewHandlerRegistration; 394 395 /** Current scroll direction. */ 396 private Direction m_scrollDirection; 397 398 /** The scroll parent. */ 399 private Element m_scrollElement; 400 401 /** Flag if automatic scrolling is enabled. */ 402 private boolean m_scrollEnabled = true; 403 404 /** Scroll timer. */ 405 private Timer m_scrollTimer; 406 407 /** The starting position absolute left. */ 408 private int m_startLeft; 409 410 /** The starting position absolute top. */ 411 private int m_startTop; 412 413 /** The registered drop targets. */ 414 private List<I_CmsDropTarget> m_targets; 415 416 /** Flag indicating whether the CTRL key was pressed when drag started. */ 417 private boolean m_modifierCTRL; 418 419 /** 420 * Constructor.<p> 421 * 422 * @param controller the drag and drop controller 423 **/ 424 public CmsDNDHandler(I_CmsDNDController controller) { 425 426 m_targets = new ArrayList<I_CmsDropTarget>(); 427 m_previewHandler = new DNDEventPreviewHandler(); 428 m_controller = controller; 429 } 430 431 /** 432 * Adds a drop target.<p> 433 * 434 * @param target the target to add 435 */ 436 public void addTarget(I_CmsDropTarget target) { 437 438 m_targets.add(target); 439 } 440 441 /** 442 * Cancels the dragging process.<p> 443 */ 444 public void cancel() { 445 446 animateCancel(m_draggable, m_controller); 447 } 448 449 /** 450 * Clears the drop target register.<p> 451 */ 452 public void clearTargets() { 453 454 m_targets.clear(); 455 } 456 457 /** 458 * Drops the draggable.<p> 459 */ 460 public void drop() { 461 462 // notifying controller, if false is returned, dropping will be canceled 463 if (!m_controller.onBeforeDrop(m_draggable, m_currentTarget, this)) { 464 cancel(); 465 return; 466 } 467 animateDrop(m_draggable, m_currentTarget, m_controller); 468 } 469 470 /** 471 * Returns the drag and drop controller.<p> 472 * 473 * @return the drag and drop controller 474 */ 475 public I_CmsDNDController getController() { 476 477 return m_controller; 478 } 479 480 /** 481 * Returns the current drop target.<p> 482 * 483 * @return the current drop target 484 */ 485 public I_CmsDropTarget getCurrentTarget() { 486 487 return m_currentTarget; 488 } 489 490 /** 491 * Returns the cursor offset x.<p> 492 * 493 * @return the cursor offset x 494 */ 495 public int getCursorOffsetX() { 496 497 return m_cursorOffsetX; 498 } 499 500 /** 501 * Returns the cursor offset y.<p> 502 * 503 * @return the cursor offset y 504 */ 505 public int getCursorOffsetY() { 506 507 return m_cursorOffsetY; 508 } 509 510 /** 511 * Returns the current draggable.<p> 512 * 513 * @return the draggable 514 */ 515 public I_CmsDraggable getDraggable() { 516 517 return m_draggable; 518 } 519 520 /** 521 * Returns the drag helper element.<p> 522 * 523 * @return the drag helper 524 */ 525 public Element getDragHelper() { 526 527 return m_dragHelper; 528 } 529 530 /** 531 * Returns the allowed drag and drop orientation.<p> 532 * 533 * @return the drag and drop orientation 534 */ 535 public Orientation getOrientation() { 536 537 return m_orientation; 538 } 539 540 /** 541 * Returns the place holder element.<p> 542 * 543 * @return the place holder element 544 */ 545 public Element getPlaceholder() { 546 547 return m_placeholder; 548 } 549 550 /** 551 * Returns whether the CTRL key was pressed when drag started.<p> 552 * 553 * @return <code>true</code> if CTRL key was pressed when drag started 554 */ 555 public boolean hasModifierCTRL() { 556 557 return m_modifierCTRL; 558 } 559 560 /** 561 * Returns if the animation is enabled.<p> 562 * 563 * @return <code>true</code> if the animation is enabled 564 */ 565 public boolean isAnimationEnabled() { 566 567 return (m_animationType != null) && (m_animationType != AnimationType.NONE); 568 } 569 570 /** 571 * Returns if a dragging process is taking place.<p> 572 * 573 * @return <code>true</code> if the handler is currently dragging 574 */ 575 public boolean isDragging() { 576 577 return m_dragging; 578 } 579 580 /** 581 * Returns if automated scrolling is enabled.<p> 582 * 583 * @return if automated scrolling is enabled 584 */ 585 public boolean isScrollEnabled() { 586 587 return m_scrollEnabled; 588 } 589 590 /** 591 * @see com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google.gwt.event.dom.client.MouseDownEvent) 592 */ 593 public void onMouseDown(MouseDownEvent event) { 594 595 if ((event.getNativeButton() != NativeEvent.BUTTON_LEFT) || m_dragging || (m_currentAnimation != null)) { 596 // only act on left button down, ignore right click 597 // also ignore if the dragging flag is still true or an animation is still running 598 return; 599 } 600 Object source = event.getSource(); 601 if (!(source instanceof I_CmsDragHandle)) { 602 // source is no drag handle, wrong DNDHandler assignment ignore 603 return; 604 } 605 I_CmsDragHandle dragHandle = (I_CmsDragHandle)source; 606 m_draggable = dragHandle.getDraggable(); 607 if (m_draggable == null) { 608 // cancel dragging 609 return; 610 } 611 m_clientX = event.getClientX(); 612 m_clientY = event.getClientY(); 613 m_cursorOffsetX = CmsDomUtil.getRelativeX(m_clientX, m_draggable.getElement()); 614 m_cursorOffsetY = CmsDomUtil.getRelativeY(m_clientY, m_draggable.getElement()); 615 m_startLeft = m_draggable.getElement().getAbsoluteLeft(); 616 m_startTop = m_draggable.getElement().getAbsoluteTop(); 617 m_currentTarget = m_draggable.getParentTarget(); 618 m_dragHelper = m_draggable.getDragHelper(m_currentTarget); 619 m_placeholder = m_draggable.getPlaceholder(m_currentTarget); 620 // notifying controller, if false is returned, dragging will be canceled 621 if (!m_controller.onDragStart(m_draggable, m_currentTarget, this)) { 622 cancel(); 623 return; 624 } 625 m_draggable.onStartDrag(m_currentTarget); 626 m_dragging = true; 627 // add marker css class to enable drag and drop dependent styles 628 Document.get().getBody().addClassName( 629 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.dragdropCss().dragStarted()); 630 if (m_scrollElement == null) { 631 CmsDomUtil.getHtmlElement().addClassName( 632 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.dragdropCss().fullWindowDrag()); 633 } 634 if (m_previewHandlerRegistration != null) { 635 // this should never be the case 636 CmsDebugLog.getInstance().printLine("Preview handler already registered!!!"); 637 m_previewHandlerRegistration.removeHandler(); 638 639 } 640 CmsDebugLog.getInstance().printLine("Registering preview handler"); 641 m_previewHandlerRegistration = Event.addNativePreviewHandler(m_previewHandler); 642 onMove((Event)event.getNativeEvent()); 643 } 644 645 /** 646 * Removes a drop target from the register.<p> 647 * 648 * @param target the target to remove 649 */ 650 public void removeTarget(I_CmsDropTarget target) { 651 652 m_targets.remove(target); 653 } 654 655 /** 656 * Sets the animation type.<p> 657 * 658 * @param animationType the animation type 659 */ 660 public void setAnimationType(AnimationType animationType) { 661 662 m_animationType = animationType; 663 } 664 665 /** 666 * Sets the drag and drop controller.<p> 667 * 668 * @param controller the drag and drop controller to set 669 */ 670 public void setController(I_CmsDNDController controller) { 671 672 m_controller = controller; 673 } 674 675 /** 676 * Sets the cursor offset x.<p> 677 * 678 * @param cursorOffsetX the cursor offset x to set 679 */ 680 public void setCursorOffsetX(int cursorOffsetX) { 681 682 m_cursorOffsetX = cursorOffsetX; 683 } 684 685 /** 686 * Sets the cursor offset y.<p> 687 * 688 * @param cursorOffsetY the cursor offset y to set 689 */ 690 public void setCursorOffsetY(int cursorOffsetY) { 691 692 m_cursorOffsetY = cursorOffsetY; 693 } 694 695 /** 696 * Sets the draggable.<p> 697 * 698 * @param draggable the draggable 699 */ 700 public void setDraggable(I_CmsDraggable draggable) { 701 702 m_draggable = draggable; 703 } 704 705 /** 706 * Sets the drag helper element.<p> 707 * 708 * @param dragHelper the drag helper element 709 */ 710 public void setDragHelper(Element dragHelper) { 711 712 m_dragHelper = dragHelper; 713 } 714 715 /** 716 * Sets the allowed drag and drop orientation.<p> 717 * 718 * @param orientation the drag and drop orientation to set 719 */ 720 public void setOrientation(Orientation orientation) { 721 722 m_orientation = orientation; 723 } 724 725 /** 726 * Sets the placeholder element.<p> 727 * 728 * @param placeholder the placeholder element 729 */ 730 public void setPlaceholder(Element placeholder) { 731 732 m_placeholder = placeholder; 733 } 734 735 /** 736 * Sets the scroll element in case not the window but another element needs scrolling.<p> 737 * 738 * @param scrollElement the scroll element 739 */ 740 public void setScrollElement(Element scrollElement) { 741 742 m_scrollElement = scrollElement; 743 } 744 745 /** 746 * Sets the scrolling enabled.<p> 747 * 748 * @param scrollEnabled <code>true</code> to enable scrolling 749 */ 750 public void setScrollEnabled(boolean scrollEnabled) { 751 752 m_scrollEnabled = scrollEnabled; 753 } 754 755 /** 756 * Sets the start position.<p> 757 * In case of a canceled drag and drop and enabled animation, 758 * the draggable helper element will be reverted to the start position.<p> 759 * Values <code><0</code> will be ignored.<p> 760 * 761 * @param left the left position 762 * @param top the top position 763 */ 764 public void setStartPosition(int left, int top) { 765 766 if (left >= 0) { 767 m_startLeft = left; 768 } 769 if (top >= 0) { 770 m_startTop = top; 771 } 772 } 773 774 /** 775 * Updates the position of the helper within the the appropriate target.<p> 776 * Needs to be executed on mouse move or when the list of allowed targets changes.<p> 777 * Uses the currently stored cursor position.<p> 778 */ 779 public void updatePosition() { 780 781 checkTargets(); 782 positionHelper(); 783 scrollAction(); 784 } 785 786 /** 787 * Clears the drag process with a move animation of the drag element to it's original position.<p> 788 * 789 * @param draggable the draggable 790 * @param controller the drag and drop controller 791 */ 792 protected void animateCancel(final I_CmsDraggable draggable, final I_CmsDNDController controller) { 793 794 controller.onAnimationStart(draggable, null, this); 795 stopDragging(); 796 Command callback = new Command() { 797 798 /** 799 * @see com.google.gwt.user.client.Command#execute() 800 */ 801 public void execute() { 802 803 controller.onDragCancel(draggable, null, CmsDNDHandler.this); 804 draggable.onDragCancel(); 805 clear(); 806 } 807 }; 808 showEndAnimation(callback, m_startTop, m_startLeft, false); 809 } 810 811 /** 812 * Clears the drag process with a move animation of the drag element to the place-holder position.<p> 813 * 814 * @param draggable the draggable 815 * @param target the drop target 816 * @param controller the drag and drop controller 817 */ 818 protected void animateDrop( 819 final I_CmsDraggable draggable, 820 final I_CmsDropTarget target, 821 final I_CmsDNDController controller) { 822 823 controller.onAnimationStart(draggable, target, this); 824 stopDragging(); 825 Command callback = new Command() { 826 827 /** 828 * @see com.google.gwt.user.client.Command#execute() 829 */ 830 public void execute() { 831 832 controller.onDrop(draggable, target, CmsDNDHandler.this); 833 target.onDrop(draggable); 834 draggable.onDrop(target); 835 clear(); 836 } 837 }; 838 CmsPositionBean placeholderPosition = CmsPositionBean.getBoundingClientRect(m_placeholder); 839 showEndAnimation(callback, placeholderPosition.getTop(), placeholderPosition.getLeft(), true); 840 } 841 842 /** 843 * Clears all references used within the current drag process.<p> 844 */ 845 protected void clear() { 846 847 for (I_CmsDropTarget target : m_targets) { 848 target.removePlaceholder(); 849 } 850 if (m_dragHelper != null) { 851 m_dragHelper.removeFromParent(); 852 m_dragHelper = null; 853 } 854 m_placeholder = null; 855 m_currentTarget = null; 856 m_draggable = null; 857 Document.get().getBody().removeClassName( 858 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.dragdropCss().dragStarted()); 859 860 CmsDomUtil.getHtmlElement().removeClassName( 861 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.dragdropCss().fullWindowDrag()); 862 863 m_currentAnimation = null; 864 m_modifierCTRL = false; 865 } 866 867 /** 868 * Cancels the scroll timer and removes the timer reference.<p> 869 */ 870 protected void clearScrollTimer() { 871 872 if (m_scrollTimer != null) { 873 m_scrollTimer.cancel(); 874 m_scrollTimer = null; 875 } 876 } 877 878 /** 879 * Execute on mouse wheel event.<p> 880 * 881 * @param event the native event 882 */ 883 protected void onMouseWheelScroll(Event event) { 884 885 int scrollStep = event.getMouseWheelVelocityY() * 5; 886 Element scrollTarget; 887 if (getCurrentTarget() != null) { 888 scrollTarget = getCurrentTarget().getElement(); 889 } else { 890 scrollTarget = RootPanel.getBodyElement(); 891 } 892 while ((scrollTarget.getScrollHeight() == scrollTarget.getClientHeight()) 893 && (scrollTarget != RootPanel.getBodyElement())) { 894 scrollTarget = scrollTarget.getParentElement(); 895 } 896 if (scrollTarget == RootPanel.getBodyElement()) { 897 int top = Window.getScrollTop() + scrollStep; 898 int left = Window.getScrollLeft(); 899 if (top < 0) { 900 top = 0; 901 } 902 Window.scrollTo(left, top); 903 } else { 904 int top = scrollTarget.getScrollTop() + scrollStep; 905 if (top < 0) { 906 top = 0; 907 } 908 scrollTarget.setScrollTop(top); 909 } 910 onMove(event); 911 } 912 913 /** 914 * Executed on mouse move while dragging.<p> 915 * 916 * @param event the event 917 */ 918 protected void onMove(Event event) { 919 920 m_clientX = event.getClientX(); 921 m_clientY = event.getClientY(); 922 updatePosition(); 923 } 924 925 /** 926 * Executed on mouse up while dragging.<p> 927 * 928 * @param event the event 929 */ 930 protected void onUp(Event event) { 931 932 m_clientX = event.getClientX(); 933 m_clientY = event.getClientY(); 934 m_modifierCTRL = event.getCtrlKey() || event.getMetaKey(); 935 if ((m_currentTarget == null) || (m_currentTarget.getPlaceholderIndex() < 0)) { 936 cancel(); 937 } else { 938 drop(); 939 } 940 } 941 942 /** 943 * Positions an element depending on the current events client position and the cursor offset. This method assumes that the element parent is positioned relative.<p> 944 */ 945 protected void positionHelper() { 946 947 if (m_dragHelper == null) { 948 // should never happen 949 CmsDebugLog.getInstance().printLine("Drag helper is null"); 950 return; 951 } 952 Element parentElement = m_dragHelper.getParentElement(); 953 int left = CmsDomUtil.getRelativeX(m_clientX, parentElement) - m_cursorOffsetX; 954 int top = CmsDomUtil.getRelativeY(m_clientY, parentElement) - m_cursorOffsetY; 955 m_dragHelper.getStyle().setLeft(left, Unit.PX); 956 m_dragHelper.getStyle().setTop(top, Unit.PX); 957 } 958 959 /** 960 * Sets dragging to false and removes the event preview handler.<p> 961 */ 962 protected void stopDragging() { 963 964 clearScrollTimer(); 965 m_dragging = false; 966 if (m_previewHandlerRegistration != null) { 967 m_previewHandlerRegistration.removeHandler(); 968 m_previewHandlerRegistration = null; 969 } 970 } 971 972 /** 973 * Method will check all registered drop targets if the element is positioned over one of them.<p> 974 */ 975 private void checkTargets() { 976 977 // checking current target first 978 if ((m_currentTarget != null) && m_currentTarget.checkPosition(m_clientX, m_clientY, m_orientation)) { 979 // in case of nested targets, check the children of the current target 980 if ((m_currentTarget instanceof I_CmsNestedDropTarget) 981 && ((I_CmsNestedDropTarget)m_currentTarget).hasDnDChildren()) { 982 for (I_CmsDropTarget target : ((I_CmsNestedDropTarget)m_currentTarget).getDnDChildren()) { 983 if ((target != m_currentTarget) && target.checkPosition(m_clientX, m_clientY, m_orientation)) { 984 // notifying controller, if false is returned, placeholder will not be positioned inside target 985 if (m_controller.onTargetEnter(m_draggable, target, this)) { 986 target.insertPlaceholder(m_placeholder, m_clientX, m_clientY, m_orientation); 987 m_currentTarget = target; 988 m_controller.onPositionedPlaceholder(m_draggable, m_currentTarget, this); 989 return; 990 } 991 } 992 } 993 } 994 if (m_currentTarget.getPlaceholderIndex() < 0) { 995 m_currentTarget.insertPlaceholder(m_placeholder, m_clientX, m_clientY, m_orientation); 996 } else { 997 m_currentTarget.repositionPlaceholder(m_clientX, m_clientY, m_orientation); 998 } 999 1000 m_controller.onPositionedPlaceholder(m_draggable, m_currentTarget, this); 1001 } else { 1002 // leaving the current target 1003 if (m_currentTarget != null) { 1004 m_controller.onTargetLeave(m_draggable, m_currentTarget, this); 1005 } 1006 for (I_CmsDropTarget target : m_targets) { 1007 if ((target != m_currentTarget) && target.checkPosition(m_clientX, m_clientY, m_orientation)) { 1008 // notifying controller, if false is returned, placeholder will not be positioned inside target 1009 if (m_controller.onTargetEnter(m_draggable, target, this)) { 1010 target.insertPlaceholder(m_placeholder, m_clientX, m_clientY, m_orientation); 1011 m_currentTarget = target; 1012 m_controller.onPositionedPlaceholder(m_draggable, m_currentTarget, this); 1013 return; 1014 } 1015 } 1016 } 1017 // mouse position is not over any registered target 1018 m_currentTarget = null; 1019 } 1020 } 1021 1022 /** 1023 * Convenience method to get the appropriate scroll direction.<p> 1024 * 1025 * @param offset the scroll parent border offset, if the cursor is within the border offset, scrolling should be triggered 1026 * 1027 * @return the scroll direction 1028 */ 1029 private Direction getScrollDirection(int offset) { 1030 1031 if (m_scrollElement == null) { 1032 Element body = RootPanel.getBodyElement(); 1033 int windowHeight = Window.getClientHeight(); 1034 int bodyHeight = body.getClientHeight(); 1035 if (windowHeight < bodyHeight) { 1036 if (((windowHeight - m_clientY) < offset) && (Window.getScrollTop() < (bodyHeight - windowHeight))) { 1037 return Direction.down; 1038 } 1039 if ((m_clientY < offset) && (Window.getScrollTop() > 0)) { 1040 return Direction.up; 1041 } 1042 } 1043 1044 int windowWidth = Window.getClientWidth(); 1045 int bodyWidth = body.getClientWidth(); 1046 if (windowWidth < bodyWidth) { 1047 if (((windowWidth - m_clientX) < offset) && (Window.getScrollLeft() < (bodyWidth - windowWidth))) { 1048 return Direction.right; 1049 } 1050 if ((m_clientX < offset) && (Window.getScrollLeft() > 0)) { 1051 return Direction.left; 1052 } 1053 } 1054 1055 return null; 1056 } else { 1057 int outerHeight = m_scrollElement.getClientHeight(); 1058 int innerHeight = m_scrollElement.getScrollHeight(); 1059 if (outerHeight < innerHeight) { 1060 if ((((outerHeight + m_scrollElement.getAbsoluteTop()) - m_clientY) < offset) 1061 && (m_scrollElement.getScrollTop() < (innerHeight - outerHeight))) { 1062 return Direction.down; 1063 } 1064 if (((m_clientY - m_scrollElement.getAbsoluteTop()) < offset) && (m_scrollElement.getScrollTop() > 0)) { 1065 return Direction.up; 1066 } 1067 } 1068 int outerWidth = m_scrollElement.getClientWidth(); 1069 int innerWidth = m_scrollElement.getScrollWidth(); 1070 if (outerWidth < innerWidth) { 1071 if ((((outerWidth + m_scrollElement.getAbsoluteLeft()) - m_clientX) < offset) 1072 && (m_scrollElement.getScrollLeft() < (innerWidth - outerWidth))) { 1073 return Direction.right; 1074 } 1075 if (((m_clientX - m_scrollElement.getAbsoluteLeft()) < offset) 1076 && (m_scrollElement.getScrollLeft() > 0)) { 1077 return Direction.left; 1078 } 1079 } 1080 return null; 1081 } 1082 } 1083 1084 /** 1085 * Handles automated scrolling.<p> 1086 */ 1087 private void scrollAction() { 1088 1089 if (m_scrollEnabled) { 1090 1091 Direction direction = getScrollDirection(50); 1092 if ((m_scrollTimer != null) && (m_scrollDirection != direction)) { 1093 clearScrollTimer(); 1094 } 1095 if ((direction != null) && (m_scrollTimer == null)) { 1096 m_scrollTimer = new CmsScrollTimer( 1097 m_scrollElement != null ? m_scrollElement : RootPanel.getBodyElement(), 1098 20, 1099 direction); 1100 m_scrollTimer.scheduleRepeating(10); 1101 } 1102 1103 m_scrollDirection = direction; 1104 } 1105 } 1106 1107 /** 1108 * Shows the end animation on drop or cancel. Executes the given callback afterwards.<p> 1109 * 1110 * @param callback the callback to execute 1111 * @param top absolute top of the animation end position 1112 * @param left absolute left of the animation end position 1113 * @param isDrop if the animation is done on drop 1114 */ 1115 private void showEndAnimation(Command callback, int top, int left, boolean isDrop) { 1116 1117 if (!isAnimationEnabled() || (m_dragHelper == null)) { 1118 callback.execute(); 1119 return; 1120 } 1121 switch (m_animationType) { 1122 case SPECIAL: 1123 List<Element> overlays = CmsDomUtil.getElementsByClass( 1124 I_CmsLayoutBundle.INSTANCE.generalCss().disablingOverlay(), 1125 m_draggable.getElement()); 1126 Element overlay = overlays.size() > 0 ? overlays.get(0) : null; 1127 m_currentAnimation = new SpecialAnimation( 1128 m_dragHelper, 1129 m_draggable.getElement(), 1130 overlay, 1131 callback, 1132 isDrop); 1133 1134 break; 1135 1136 case MOVE: 1137 Element parentElement = m_dragHelper.getParentElement(); 1138 int endTop = top - parentElement.getAbsoluteTop(); 1139 int endLeft = left - parentElement.getAbsoluteLeft(); 1140 int startTop = CmsDomUtil.getCurrentStyleInt(m_dragHelper, Style.top); 1141 int startLeft = CmsDomUtil.getCurrentStyleInt(m_dragHelper, Style.left); 1142 m_currentAnimation = new CmsMoveAnimation(m_dragHelper, startTop, startLeft, endTop, endLeft, callback); 1143 break; 1144 1145 default: 1146 // nothing to do 1147 } 1148 m_currentAnimation.run(400); 1149 } 1150}