1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.custom;
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.dnd.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.widgets.*;
22 * This adapter class provides a default drag under effect (eg. select and scroll)
23 * when a drag occurs over a <code>StyledText</code>.
25 * <p>Classes that wish to provide their own drag under effect for a <code>StyledText</code>
26 * can extend this class, override the <code>StyledTextDropTargetEffect.dragOver</code>
27 * method and override any other applicable methods in <code>StyledTextDropTargetEffect</code> to
28 * display their own drag under effect.</p>
30 * Subclasses that override any methods of this class should call the corresponding
31 * <code>super</code> method to get the default drag under effect implementation.
33 * <p>The feedback value is either one of the FEEDBACK constants defined in
34 * class <code>DND</code> which is applicable to instances of this class,
35 * or it must be built by <em>bitwise OR</em>'ing together
36 * (that is, using the <code>int</code> "|" operator) two or more
37 * of those <code>DND</code> effect constants.
40 * <dt><b>Feedback:</b></dt>
41 * <dd>FEEDBACK_SELECT, FEEDBACK_SCROLL</dd>
44 * @see DropTargetAdapter
45 * @see DropTargetEvent
46 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
50 public class StyledTextDropTargetEffect extends DropTargetEffect {
51 static final int CARET_WIDTH = 2;
52 static final int SCROLL_HYSTERESIS = 100; // milli seconds
53 static final int SCROLL_TOLERANCE = 20; // pixels
55 int currentOffset = -1;
57 int scrollX = -1, scrollY = -1;
58 Listener paintListener;
61 * Creates a new <code>StyledTextDropTargetEffect</code> to handle the drag under effect on the specified
62 * <code>StyledText</code>.
64 * @param styledText the <code>StyledText</code> over which the user positions the cursor to drop the data
66 public StyledTextDropTargetEffect(StyledText styledText) {
68 paintListener = event -> {
69 if (currentOffset != -1) {
70 StyledText text = (StyledText) getControl();
71 Point position = text.getLocationAtOffset(currentOffset);
72 int height = text.getLineHeight(currentOffset);
73 event.gc.setBackground(event.display.getSystemColor (SWT.COLOR_BLACK));
74 event.gc.fillRectangle(position.x, position.y, CARET_WIDTH, height);
80 * This implementation of <code>dragEnter</code> provides a default drag under effect
81 * for the feedback specified in <code>event.feedback</code>.
83 * For additional information see <code>DropTargetAdapter.dragEnter</code>.
85 * Subclasses that override this method should call <code>super.dragEnter(event)</code>
86 * to get the default drag under effect implementation.
88 * @param event the information associated with the drag start event
90 * @see DropTargetAdapter
91 * @see DropTargetEvent
94 public void dragEnter(DropTargetEvent event) {
99 getControl().removeListener(SWT.Paint, paintListener);
100 getControl().addListener (SWT.Paint, paintListener);
104 * This implementation of <code>dragLeave</code> provides a default drag under effect
105 * for the feedback specified in <code>event.feedback</code>.
107 * For additional information see <code>DropTargetAdapter.dragLeave</code>.
109 * Subclasses that override this method should call <code>super.dragLeave(event)</code>
110 * to get the default drag under effect implementation.
112 * @param event the information associated with the drag leave event
114 * @see DropTargetAdapter
115 * @see DropTargetEvent
118 public void dragLeave(DropTargetEvent event) {
119 StyledText text = (StyledText) getControl();
120 if (currentOffset != -1) {
121 refreshCaret(text, currentOffset, -1);
123 text.removeListener(SWT.Paint, paintListener);
130 * This implementation of <code>dragOver</code> provides a default drag under effect
131 * for the feedback specified in <code>event.feedback</code>.
133 * For additional information see <code>DropTargetAdapter.dragOver</code>.
135 * Subclasses that override this method should call <code>super.dragOver(event)</code>
136 * to get the default drag under effect implementation.
138 * @param event the information associated with the drag over event
140 * @see DropTargetAdapter
141 * @see DropTargetEvent
142 * @see DND#FEEDBACK_SELECT
143 * @see DND#FEEDBACK_SCROLL
146 public void dragOver(DropTargetEvent event) {
147 int effect = event.feedback;
148 StyledText text = (StyledText) getControl();
150 Point pt = text.getDisplay().map(null, text, event.x, event.y);
151 if ((effect & DND.FEEDBACK_SCROLL) == 0) {
153 scrollX = scrollY = -1;
155 if (text.getCharCount() == 0) {
157 scrollX = scrollY = -1;
159 if (scrollX != -1 && scrollY != -1 && scrollBeginTime != 0 &&
160 (pt.x >= scrollX && pt.x <= (scrollX + SCROLL_TOLERANCE) ||
161 pt.y >= scrollY && pt.y <= (scrollY + SCROLL_TOLERANCE))) {
162 if (System.currentTimeMillis() >= scrollBeginTime) {
163 Rectangle area = text.getClientArea();
164 GC gc = new GC(text);
165 FontMetrics fm = gc.getFontMetrics();
167 double charWidth = fm.getAverageCharacterWidth();
168 int scrollAmount = (int) (10*charWidth);
169 if (pt.x < area.x + 3*charWidth) {
170 int leftPixel = text.getHorizontalPixel();
171 text.setHorizontalPixel(leftPixel - scrollAmount);
173 if (pt.x > area.width - 3*charWidth) {
174 int leftPixel = text.getHorizontalPixel();
175 text.setHorizontalPixel(leftPixel + scrollAmount);
177 int lineHeight = text.getLineHeight();
178 if (pt.y < area.y + lineHeight) {
179 int topPixel = text.getTopPixel();
180 text.setTopPixel(topPixel - lineHeight);
182 if (pt.y > area.height - lineHeight) {
183 int topPixel = text.getTopPixel();
184 text.setTopPixel(topPixel + lineHeight);
187 scrollX = scrollY = -1;
190 scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS;
197 if ((effect & DND.FEEDBACK_SELECT) != 0) {
198 int[] trailing = new int [1];
199 int newOffset = text.getOffsetAtPoint(pt.x, pt.y, trailing, false);
200 newOffset += trailing [0];
201 if (newOffset != currentOffset) {
202 refreshCaret(text, currentOffset, newOffset);
203 currentOffset = newOffset;
208 void refreshCaret(StyledText text, int oldOffset, int newOffset) {
209 if (oldOffset != newOffset) {
210 if (oldOffset != -1) {
211 Point oldPos = text.getLocationAtOffset(oldOffset);
212 int oldHeight = text.getLineHeight(oldOffset);
213 text.redraw (oldPos.x, oldPos.y, CARET_WIDTH, oldHeight, false);
215 if (newOffset != -1) {
216 Point newPos = text.getLocationAtOffset(newOffset);
217 int newHeight = text.getLineHeight(newOffset);
218 text.redraw (newPos.x, newPos.y, CARET_WIDTH, newHeight, false);
224 * This implementation of <code>dropAccept</code> provides a default drag under effect
225 * for the feedback specified in <code>event.feedback</code>.
227 * For additional information see <code>DropTargetAdapter.dropAccept</code>.
229 * Subclasses that override this method should call <code>super.dropAccept(event)</code>
230 * to get the default drag under effect implementation.
232 * @param event the information associated with the drop accept event
234 * @see DropTargetAdapter
235 * @see DropTargetEvent
238 public void dropAccept(DropTargetEvent event) {
239 if (currentOffset != -1) {
240 StyledText text = (StyledText) getControl();
241 text.setSelection(currentOffset);