Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / examples / ux / DataView / DragSelector.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.ux.DataView.DragSelector
17  * @extends Object
18  * @author Ed Spencer
19  * 
20  */
21 Ext.define('Ext.ux.DataView.DragSelector', {
22     requires: ['Ext.dd.DragTracker', 'Ext.util.Region'],
23
24     /**
25      * Initializes the plugin by setting up the drag tracker
26      */
27     init: function(dataview) {
28         /**
29          * @property dataview
30          * @type Ext.view.View
31          * The DataView bound to this instance
32          */
33         this.dataview = dataview;
34         dataview.mon(dataview, {
35             beforecontainerclick: this.cancelClick,
36             scope: this,
37             render: {
38                 fn: this.onRender,
39                 scope: this,
40                 single: true
41             }
42         });
43     },
44
45     /**
46      * @private
47      * Called when the attached DataView is rendered. This sets up the DragTracker instance that will be used
48      * to created a dragged selection area
49      */
50     onRender: function() {
51         /**
52          * @property tracker
53          * @type Ext.dd.DragTracker
54          * The DragTracker attached to this instance. Note that the 4 on* functions are called in the scope of the 
55          * DragTracker ('this' refers to the DragTracker inside those functions), so we pass a reference to the 
56          * DragSelector so that we can call this class's functions.
57          */
58         this.tracker = Ext.create('Ext.dd.DragTracker', {
59             dataview: this.dataview,
60             el: this.dataview.el,
61             dragSelector: this,
62             onBeforeStart: this.onBeforeStart,
63             onStart: this.onStart,
64             onDrag : this.onDrag,
65             onEnd  : this.onEnd
66         });
67
68         /**
69          * @property dragRegion
70          * @type Ext.util.Region
71          * Represents the region currently dragged out by the user. This is used to figure out which dataview nodes are
72          * in the selected area and to set the size of the Proxy element used to highlight the current drag area
73          */
74         this.dragRegion = Ext.create('Ext.util.Region');
75     },
76
77     /**
78      * @private
79      * Listener attached to the DragTracker's onBeforeStart event. Returns false if the drag didn't start within the
80      * DataView's el
81      */
82     onBeforeStart: function(e) {
83         return e.target == this.dataview.getEl().dom;
84     },
85
86     /**
87      * @private
88      * Listener attached to the DragTracker's onStart event. Cancel's the DataView's containerclick event from firing
89      * and sets the start co-ordinates of the Proxy element. Clears any existing DataView selection
90      * @param {EventObject} e The click event
91      */
92     onStart: function(e) {
93         var dragSelector = this.dragSelector,
94             dataview     = this.dataview;
95
96         // Flag which controls whether the cancelClick method vetoes the processing of the DataView's containerclick event.
97         // On IE (where else), this needs to remain set for a millisecond after mouseup because even though the mouse has
98         // moved, the mouseup will still trigger a click event.
99         this.dragging = true;
100
101         //here we reset and show the selection proxy element and cache the regions each item in the dataview take up
102         dragSelector.fillRegions();
103         dragSelector.getProxy().show();
104         dataview.getSelectionModel().deselectAll();
105     },
106
107     /**
108      * @private
109      * Reusable handler that's used to cancel the container click event when dragging on the dataview. See onStart for
110      * details
111      */
112     cancelClick: function() {
113         return !this.tracker.dragging;
114     },
115
116     /**
117      * @private
118      * Listener attached to the DragTracker's onDrag event. Figures out how large the drag selection area should be and
119      * updates the proxy element's size to match. Then iterates over all of the rendered items and marks them selected
120      * if the drag region touches them
121      * @param {EventObject} e The drag event
122      */
123     onDrag: function(e) {
124         var dragSelector = this.dragSelector,
125             selModel     = dragSelector.dataview.getSelectionModel(),
126             dragRegion   = dragSelector.dragRegion,
127             bodyRegion   = dragSelector.bodyRegion,
128             proxy        = dragSelector.getProxy(),
129             regions      = dragSelector.regions,
130             length       = regions.length,
131
132             startXY   = this.startXY,
133             currentXY = this.getXY(),
134             minX      = Math.min(startXY[0], currentXY[0]),
135             minY      = Math.min(startXY[1], currentXY[1]),
136             width     = Math.abs(startXY[0] - currentXY[0]),
137             height    = Math.abs(startXY[1] - currentXY[1]),
138             region, selected, i;
139
140         Ext.apply(dragRegion, {
141             top: minY,
142             left: minX,
143             right: minX + width,
144             bottom: minY + height
145         });
146
147         dragRegion.constrainTo(bodyRegion);
148         proxy.setRegion(dragRegion);
149
150         for (i = 0; i < length; i++) {
151             region = regions[i];
152             selected = dragRegion.intersect(region);
153
154             if (selected) {
155                 selModel.select(i, true);
156             } else {
157                 selModel.deselect(i);
158             }
159         }
160     },
161
162     /**
163      * @private
164      * Listener attached to the DragTracker's onEnd event. This is a delayed function which executes 1
165      * millisecond after it has been called. This is because the dragging flag must remain active to cancel
166      * the containerclick event which the mouseup event will trigger.
167      * @param {EventObject} e The event object
168      */
169     onEnd: Ext.Function.createDelayed(function(e) {
170         var dataview = this.dataview,
171             selModel = dataview.getSelectionModel(),
172             dragSelector = this.dragSelector;
173
174         this.dragging = false;
175         dragSelector.getProxy().hide();
176     }, 1),
177
178     /**
179      * @private
180      * Creates a Proxy element that will be used to highlight the drag selection region
181      * @return {Ext.Element} The Proxy element
182      */
183     getProxy: function() {
184         if (!this.proxy) {
185             this.proxy = this.dataview.getEl().createChild({
186                 tag: 'div',
187                 cls: 'x-view-selector'
188             });
189         }
190         return this.proxy;
191     },
192
193     /**
194      * @private
195      * Gets the region taken up by each rendered node in the DataView. We use these regions to figure out which nodes
196      * to select based on the selector region the user has dragged out
197      */
198     fillRegions: function() {
199         var dataview = this.dataview,
200             regions  = this.regions = [];
201
202         dataview.all.each(function(node) {
203             regions.push(node.getRegion());
204         });
205         this.bodyRegion = dataview.getEl().getRegion();
206     }
207 });