root/dwt/dnd/TreeDropTargetEffect.d

Revision 246:fd9c62a2998e, 11.6 kB (checked in by Frank Benoit <benoit@tionex.de>, 5 months ago)

Updater SWT 3.4M7 to 3.4

Line 
1 /*******************************************************************************
2  * Copyright (c) 2007, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.dnd.TreeDropTargetEffect;
14
15 import dwt.DWT;
16 import dwt.graphics.Point;
17 import dwt.internal.win32.OS;
18 import dwt.widgets.Event;
19 import dwt.widgets.Tree;
20 import dwt.widgets.TreeItem;
21
22 import dwt.dnd.DropTargetEffect;
23 import dwt.dnd.DropTargetEvent;
24 import dwt.dnd.DND;
25
26 import dwt.dwthelper.utils;
27
28 /**
29  * This class provides a default drag under effect (eg. select, insert, scroll and expand)
30  * when a drag occurs over a <code>Tree</code>.
31  *
32  * <p>Classes that wish to provide their own drag under effect for a <code>Tree</code>
33  * can extend the <code>TreeDropTargetEffect</code> class and override any applicable methods
34  * in <code>TreeDropTargetEffect</code> to display their own drag under effect.</p>
35  *
36  * Subclasses that override any methods of this class must call the corresponding
37  * <code>super</code> method to get the default drag under effect implementation.
38  *
39  * <p>The feedback value is either one of the FEEDBACK constants defined in
40  * class <code>DND</code> which is applicable to instances of this class,
41  * or it must be built by <em>bitwise OR</em>'ing together
42  * (that is, using the <code>int</code> "|" operator) two or more
43  * of those <code>DND</code> effect constants.
44  * </p>
45  * <p>
46  * <dl>
47  * <dt><b>Feedback:</b></dt>
48  * <dd>FEEDBACK_SELECT, FEEDBACK_INSERT_BEFORE, FEEDBACK_INSERT_AFTER, FEEDBACK_EXPAND, FEEDBACK_SCROLL</dd>
49  * </dl>
50  * </p><p>
51  * Note: Only one of the styles FEEDBACK_SELECT, FEEDBACK_INSERT_BEFORE or
52  * FEEDBACK_INSERT_AFTER may be specified.
53  * </p>
54  *
55  * @see DropTargetAdapter
56  * @see DropTargetEvent
57  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
58  *
59  * @since 3.3
60  */
61 public class TreeDropTargetEffect : DropTargetEffect {
62     static final int SCROLL_HYSTERESIS = 200; // milli seconds
63     static final int EXPAND_HYSTERESIS = 1000; // milli seconds
64
65     int /*long*/ dropIndex;
66     int scrollIndex;
67     long scrollBeginTime;
68     int /*long*/ expandIndex;
69     long expandBeginTime;
70     TreeItem insertItem;
71     bool insertBefore;
72
73     /**
74      * Creates a new <code>TreeDropTargetEffect</code> to handle the drag under effect on the specified
75      * <code>Tree</code>.
76      *
77      * @param tree the <code>Tree</code> over which the user positions the cursor to drop the data
78      */
79     public this(Tree tree) {
80         super(tree);
81     }
82
83     int checkEffect(int effect) {
84         // Some effects are mutually exclusive.  Make sure that only one of the mutually exclusive effects has been specified.
85         if ((effect & DND.FEEDBACK_SELECT) !is 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER & ~DND.FEEDBACK_INSERT_BEFORE;
86         if ((effect & DND.FEEDBACK_INSERT_BEFORE) !is 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER;
87         return effect;
88     }
89
90     /**
91      * This implementation of <code>dragEnter</code> provides a default drag under effect
92      * for the feedback specified in <code>event.feedback</code>.
93      *
94      * For additional information see <code>DropTargetAdapter.dragEnter</code>.
95      *
96      * Subclasses that override this method should call <code>super.dragEnter(event)</code>
97      * to get the default drag under effect implementation.
98      *
99      * @param event  the information associated with the drag enter event
100      *
101      * @see DropTargetAdapter
102      * @see DropTargetEvent
103      */
104     public void dragEnter(DropTargetEvent event) {
105         dropIndex = -1;
106         insertItem = null;
107         expandBeginTime = 0;
108         expandIndex = -1;
109         scrollBeginTime = 0;
110         scrollIndex = -1;
111     }
112
113     /**
114      * This implementation of <code>dragLeave</code> provides a default drag under effect
115      * for the feedback specified in <code>event.feedback</code>.
116      *
117      * For additional information see <code>DropTargetAdapter.dragLeave</code>.
118      *
119      * Subclasses that override this method should call <code>super.dragLeave(event)</code>
120      * to get the default drag under effect implementation.
121      *
122      * @param event  the information associated with the drag leave event
123      *
124      * @see DropTargetAdapter
125      * @see DropTargetEvent
126      */
127     public void dragLeave(DropTargetEvent event) {
128         Tree tree = cast(Tree) control;
129         auto handle = tree.handle;
130         if (dropIndex !is -1) {
131             TVITEM tvItem;
132             tvItem.hItem = cast(HTREEITEM) dropIndex;
133             tvItem.mask = OS.TVIF_STATE;
134             tvItem.stateMask = OS.TVIS_DROPHILITED;
135             tvItem.state = 0;
136             OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem);
137             dropIndex = -1;
138         }
139         if (insertItem !is null) {
140             tree.setInsertMark(null, false);
141             insertItem = null;
142         }
143         expandBeginTime = 0;
144         expandIndex = -1;
145         scrollBeginTime = 0;
146         scrollIndex = -1;
147     }
148
149     /**
150      * This implementation of <code>dragOver</code> provides a default drag under effect
151      * for the feedback specified in <code>event.feedback</code>.
152      *
153      * For additional information see <code>DropTargetAdapter.dragOver</code>.
154      *
155      * Subclasses that override this method should call <code>super.dragOver(event)</code>
156      * to get the default drag under effect implementation.
157      *
158      * @param event  the information associated with the drag over event
159      *
160      * @see DropTargetAdapter
161      * @see DropTargetEvent
162      * @see DND#FEEDBACK_SELECT
163      * @see DND#FEEDBACK_INSERT_BEFORE
164      * @see DND#FEEDBACK_INSERT_AFTER
165      * @see DND#FEEDBACK_SCROLL
166      */
167     public void dragOver(DropTargetEvent event) {
168         Tree tree = cast(Tree) getControl();
169         int effect = checkEffect(event.feedback);
170         auto handle = tree.handle;
171         Point coordinates = new Point(event.x, event.y);
172         coordinates = tree.toControl(coordinates);
173         TVHITTESTINFO lpht;
174         lpht.pt.x = coordinates.x;
175         lpht.pt.y = coordinates.y;
176         OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht);
177         auto hItem = lpht.hItem;
178         if ((effect & DND.FEEDBACK_SCROLL) is 0) {
179             scrollBeginTime = 0;
180             scrollIndex = -1;
181         } else {
182             if (hItem !is cast(HTREEITEM)-1 && cast(HTREEITEM)scrollIndex is hItem && scrollBeginTime !is 0) {
183                 if (System.currentTimeMillis() >= scrollBeginTime) {
184                     auto topItem = cast(HTREEITEM)OS.SendMessage(handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
185                     auto nextItem = cast(HTREEITEM)OS.SendMessage(handle, OS.TVM_GETNEXTITEM, hItem is topItem ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE, hItem);
186                     bool scroll = true;
187                     if (hItem is topItem) {
188                         scroll = nextItem !is null;
189                     } else {
190                         RECT itemRect;
191                         if (OS.TreeView_GetItemRect (handle, nextItem, &itemRect, true)) {
192                             RECT rect;
193                             OS.GetClientRect (handle, &rect);
194                             POINT pt;
195                             pt.x = itemRect.left;
196                             pt.y = itemRect.top;
197                             if (OS.PtInRect (&rect, pt)) {
198                                 pt.y = itemRect.bottom;
199                                 if (OS.PtInRect (&rect, pt)) scroll = false;
200                             }
201                         }
202                     }
203                     if (scroll) {
204                         OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, nextItem);
205                         tree.redraw();
206                     }
207                     scrollBeginTime = 0;
208                     scrollIndex = -1;
209                 }
210             } else {
211                 scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS;
212                 scrollIndex = cast(int)hItem;
213             }
214         }
215         if ((effect & DND.FEEDBACK_EXPAND) is 0) {
216             expandBeginTime = 0;
217             expandIndex = -1;
218         } else {
219             if (cast(int)hItem !is -1 && expandIndex is cast(int)hItem && expandBeginTime !is 0) {
220                 if (System.currentTimeMillis() >= expandBeginTime) {
221                     if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem) !is 0) {
222                         TreeItem item = cast(TreeItem)tree.getDisplay().findWidget(tree.handle, cast(int)hItem);
223                         if (item !is null && !item.getExpanded()) {
224                             item.setExpanded(true);
225                             tree.redraw();
226                             Event expandEvent = new Event ();
227                             expandEvent.item = item;
228                             tree.notifyListeners(DWT.Expand, expandEvent);
229                         }
230                     }
231                     expandBeginTime = 0;
232                     expandIndex = -1;
233                 }
234             } else {
235                 expandBeginTime = System.currentTimeMillis() + EXPAND_HYSTERESIS;
236                 expandIndex = cast(int)hItem;
237             }
238         }
239         if (dropIndex !is -1 && (dropIndex !is cast(int)hItem || (effect & DND.FEEDBACK_SELECT) is 0)) {
240             TVITEM tvItem;
241             tvItem.hItem = cast(HTREEITEM) dropIndex;
242             tvItem.mask = OS.TVIF_STATE;
243             tvItem.stateMask = OS.TVIS_DROPHILITED;
244             tvItem.state = 0;
245             OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem);
246             dropIndex = -1;
247         }
248         if (cast(int)hItem !is -1 && cast(int)hItem !is dropIndex && (effect & DND.FEEDBACK_SELECT) !is 0) {
249             TVITEM tvItem;
250             tvItem.hItem = cast(HTREEITEM) hItem;
251             tvItem.mask = OS.TVIF_STATE;
252             tvItem.stateMask = OS.TVIS_DROPHILITED;
253             tvItem.state = OS.TVIS_DROPHILITED;
254             OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem);
255             dropIndex = cast(int)hItem;
256         }
257         if ((effect & DND.FEEDBACK_INSERT_BEFORE) !is 0 || (effect & DND.FEEDBACK_INSERT_AFTER) !is 0) {
258             bool before = (effect & DND.FEEDBACK_INSERT_BEFORE) !is 0;
259             /*
260             * Bug in Windows.  When TVM_SETINSERTMARK is used to set
261             * an insert mark for a tree and an item is expanded or
262             * collapsed near the insert mark, the tree does not redraw
263             * the insert mark properly.  The fix is to hide and show
264             * the insert mark whenever an item is expanded or collapsed.
265             * Since the insert mark can not be queried from the tree,
266             * use the Tree API rather than calling the OS directly.
267             */
268             TreeItem item = cast(TreeItem)tree.getDisplay().findWidget(tree.handle, cast(int)hItem);
269             if (item !is null) {
270                 if (item !is insertItem || before !is insertBefore) {
271                     tree.setInsertMark(item, before);
272                 }
273                 insertItem = item;
274                 insertBefore = before;
275             } else {
276                 if (insertItem !is null) {
277                     tree.setInsertMark(null, false);
278                 }
279                 insertItem = null;
280             }
281         } else {
282             if (insertItem !is null) {
283                 tree.setInsertMark(null, false);
284             }
285             insertItem = null;
286         }
287     }
288 }
Note: See TracBrowser for help on using the browser.