@@ -12,6 +12,43 @@ import { activeElt } from "../util/dom"
1212import { e_button , e_defaultPrevented , e_preventDefault , e_target , hasHandler , off , on , signal , signalDOMEvent } from "../util/event"
1313import { dragAndDrop } from "../util/feature_detection"
1414import { bind , countColumn , findColumn , sel_mouse } from "../util/misc"
15+ import { addModifierNames } from "../input/keymap"
16+ import { Pass } from "../util/misc"
17+
18+ import { dispatchKey } from "./key_events"
19+ import { commands } from "./commands"
20+
21+ const DOUBLECLICK_DELAY = 400
22+
23+ class PastClick {
24+ constructor ( time , pos , button ) {
25+ this . time = time
26+ this . pos = pos
27+ this . button = button
28+ }
29+
30+ compare ( time , pos , button ) {
31+ return this . time + DOUBLECLICK_DELAY > time &&
32+ cmp ( pos , this . pos ) == 0 && button == this . button
33+ }
34+ }
35+
36+ let lastClick , lastDoubleClick
37+ function clickRepeat ( pos , button ) {
38+ let now = + new Date
39+ if ( lastDoubleClick && lastDoubleClick . compare ( now , pos , button ) ) {
40+ lastClick = lastDoubleClick = null
41+ return "triple"
42+ } else if ( lastClick && lastClick . compare ( now , pos , button ) ) {
43+ lastDoubleClick = new PastClick ( now , pos , button )
44+ lastClick = null
45+ return "double"
46+ } else {
47+ lastClick = new PastClick ( now , pos , button )
48+ lastDoubleClick = null
49+ return "single"
50+ }
51+ }
1552
1653// A mouse down can be a single click, double click, triple click,
1754// start of selection drag, start of text drag, new cursor
@@ -34,61 +71,79 @@ export function onMouseDown(e) {
3471 return
3572 }
3673 if ( clickInGutter ( cm , e ) ) return
37- let start = posFromMouse ( cm , e )
74+ let pos = posFromMouse ( cm , e ) , button = e_button ( e ) , repeat = pos ? clickRepeat ( pos , button ) : "single"
3875 window . focus ( )
3976
40- switch ( e_button ( e ) ) {
41- case 1 :
42- // #3261: make sure, that we're not starting a second selection
43- if ( cm . state . selectingText )
44- cm . state . selectingText ( e )
45- else if ( start )
46- leftButtonDown ( cm , e , start )
47- else if ( e_target ( e ) == display . scroller )
48- e_preventDefault ( e )
49- break
50- case 2 :
51- if ( webkit ) cm . state . lastMiddleDown = + new Date
52- if ( start ) extendSelection ( cm . doc , start )
77+ // #3261: make sure, that we're not starting a second selection
78+ if ( button == 1 && cm . state . selectingText )
79+ cm . state . selectingText ( e )
80+
81+ if ( pos && handleMappedButton ( cm , button , pos , repeat , e ) ) return
82+
83+ if ( button == 1 ) {
84+ if ( pos ) leftButtonDown ( cm , pos , repeat , e )
85+ else if ( e_target ( e ) == display . scroller ) e_preventDefault ( e )
86+ } else if ( button == 2 ) {
87+ if ( pos ) extendSelection ( cm . doc , pos )
5388 setTimeout ( ( ) => display . input . focus ( ) , 20 )
54- e_preventDefault ( e )
55- break
56- case 3 :
89+ } else if ( button == 3 ) {
5790 if ( captureRightClick ) onContextMenu ( cm , e )
5891 else delayBlurEvent ( cm )
59- break
6092 }
6193}
6294
63- let lastClick , lastDoubleClick
64- function leftButtonDown ( cm , e , start ) {
95+ function handleMappedButton ( cm , button , pos , repeat , event ) {
96+ let name = "Click"
97+ if ( repeat == "double" ) name = "Double" + name
98+ else if ( repeat == "triple" ) name = "Triple" + name
99+ name = ( button == 1 ? "Left" : button == 2 ? "Middle" : "Right" ) + name
100+
101+ return dispatchKey ( cm , addModifierNames ( name , event ) , event , bound => {
102+ if ( typeof bound == "string" ) bound = commands [ bound ]
103+ if ( ! bound ) return false
104+ let done = false
105+ try {
106+ if ( cm . isReadOnly ( ) ) cm . state . suppressEdits = true
107+ done = bound ( cm , pos ) != Pass
108+ } finally {
109+ cm . state . suppressEdits = false
110+ }
111+ return done
112+ } )
113+ }
114+
115+ function configureMouse ( cm , repeat , event ) {
116+ let option = cm . getOption ( "configureMouse" )
117+ let value = option ? option ( cm , repeat , event ) : { }
118+ if ( value . unit == null ) {
119+ let rect = chromeOS ? event . shiftKey && event . metaKey : event . altKey
120+ value . unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"
121+ }
122+ if ( value . extend == null || cm . doc . extend ) value . extend = cm . doc . extend || event . shiftKey
123+ if ( value . addNew == null ) value . addNew = mac ? event . metaKey : event . ctrlKey
124+ if ( value . moveOnDrag == null ) value . moveOnDrag = ! ( mac ? event . altKey : event . ctrlKey )
125+ return value
126+ }
127+
128+ function leftButtonDown ( cm , pos , repeat , event ) {
65129 if ( ie ) setTimeout ( bind ( ensureFocus , cm ) , 0 )
66130 else cm . curOp . focus = activeElt ( )
67131
68- let now = + new Date , type
69- if ( lastDoubleClick && lastDoubleClick . time > now - 400 && cmp ( lastDoubleClick . pos , start ) == 0 ) {
70- type = "triple"
71- } else if ( lastClick && lastClick . time > now - 400 && cmp ( lastClick . pos , start ) == 0 ) {
72- type = "double"
73- lastDoubleClick = { time : now , pos : start }
74- } else {
75- type = "single"
76- lastClick = { time : now , pos : start }
77- }
132+ let behavior = configureMouse ( cm , repeat , event )
78133
79- let sel = cm . doc . sel , modifier = mac ? e . metaKey : e . ctrlKey , contained
134+ let sel = cm . doc . sel , contained
80135 if ( cm . options . dragDrop && dragAndDrop && ! cm . isReadOnly ( ) &&
81- type == "single" && ( contained = sel . contains ( start ) ) > - 1 &&
82- ( cmp ( ( contained = sel . ranges [ contained ] ) . from ( ) , start ) < 0 || start . xRel > 0 ) &&
83- ( cmp ( contained . to ( ) , start ) > 0 || start . xRel < 0 ) )
84- leftButtonStartDrag ( cm , e , start , modifier )
136+ repeat == "single" && ( contained = sel . contains ( pos ) ) > - 1 &&
137+ ( cmp ( ( contained = sel . ranges [ contained ] ) . from ( ) , pos ) < 0 || pos . xRel > 0 ) &&
138+ ( cmp ( contained . to ( ) , pos ) > 0 || pos . xRel < 0 ) )
139+ leftButtonStartDrag ( cm , event , pos , behavior )
85140 else
86- leftButtonSelect ( cm , e , start , type , modifier )
141+ leftButtonSelect ( cm , event , pos , behavior )
87142}
88143
89144// Start a text drag. When it ends, see if any dragging actually
90145// happen, and treat as a click if it didn't.
91- function leftButtonStartDrag ( cm , e , start , modifier ) {
146+ function leftButtonStartDrag ( cm , event , pos , behavior ) {
92147 let display = cm . display , moved = false
93148 let dragEnd = operation ( cm , e => {
94149 if ( webkit ) display . scroller . draggable = false
@@ -99,8 +154,8 @@ function leftButtonStartDrag(cm, e, start, modifier) {
99154 off ( display . scroller , "drop" , dragEnd )
100155 if ( ! moved ) {
101156 e_preventDefault ( e )
102- if ( ! modifier )
103- extendSelection ( cm . doc , start )
157+ if ( ! behavior . addNew )
158+ extendSelection ( cm . doc , pos , null , null , behavior . extend )
104159 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
105160 if ( webkit || ie && ie_version == 9 )
106161 setTimeout ( ( ) => { document . body . focus ( ) ; display . input . focus ( ) } , 20 )
@@ -109,13 +164,13 @@ function leftButtonStartDrag(cm, e, start, modifier) {
109164 }
110165 } )
111166 let mouseMove = function ( e2 ) {
112- moved = moved || Math . abs ( e . clientX - e2 . clientX ) + Math . abs ( e . clientY - e2 . clientY ) >= 10
167+ moved = moved || Math . abs ( event . clientX - e2 . clientX ) + Math . abs ( event . clientY - e2 . clientY ) >= 10
113168 }
114169 let dragStart = ( ) => moved = true
115170 // Let the drag handler handle this.
116171 if ( webkit ) display . scroller . draggable = true
117172 cm . state . draggingText = dragEnd
118- dragEnd . copy = mac ? e . altKey : e . ctrlKey
173+ dragEnd . copy = ! behavior . moveOnDrag
119174 // IE's approach to draggable
120175 if ( display . scroller . dragDrop ) display . scroller . dragDrop ( )
121176 on ( document , "mouseup" , dragEnd )
@@ -127,13 +182,21 @@ function leftButtonStartDrag(cm, e, start, modifier) {
127182 setTimeout ( ( ) => display . input . focus ( ) , 20 )
128183}
129184
185+ function rangeForUnit ( cm , pos , unit ) {
186+ if ( unit == "char" ) return new Range ( pos , pos )
187+ if ( unit == "word" ) return cm . findWordAt ( pos )
188+ if ( unit == "line" ) return new Range ( Pos ( pos . line , 0 ) , clipPos ( cm . doc , Pos ( pos . line + 1 , 0 ) ) )
189+ let result = unit ( cm , pos )
190+ return new Range ( result . from , result . to )
191+ }
192+
130193// Normal selection, as opposed to text dragging.
131- function leftButtonSelect ( cm , e , start , type , addNew ) {
194+ function leftButtonSelect ( cm , event , start , behavior ) {
132195 let display = cm . display , doc = cm . doc
133- e_preventDefault ( e )
196+ e_preventDefault ( event )
134197
135198 let ourRange , ourIndex , startSel = doc . sel , ranges = startSel . ranges
136- if ( addNew && ! e . shiftKey ) {
199+ if ( behavior . addNew && ! behavior . extend ) {
137200 ourIndex = doc . sel . contains ( start )
138201 if ( ourIndex > - 1 )
139202 ourRange = ranges [ ourIndex ]
@@ -144,36 +207,27 @@ function leftButtonSelect(cm, e, start, type, addNew) {
144207 ourIndex = doc . sel . primIndex
145208 }
146209
147- if ( chromeOS ? e . shiftKey && e . metaKey : e . altKey ) {
148- type = "rect"
149- if ( ! addNew ) ourRange = new Range ( start , start )
150- start = posFromMouse ( cm , e , true , true )
210+ if ( behavior . unit == "rectangle" ) {
211+ if ( ! behavior . addNew ) ourRange = new Range ( start , start )
212+ start = posFromMouse ( cm , event , true , true )
151213 ourIndex = - 1
152- } else if ( type == "double" ) {
153- let word = cm . findWordAt ( start )
154- if ( cm . display . shift || doc . extend )
155- ourRange = extendRange ( doc , ourRange , word . anchor , word . head )
156- else
157- ourRange = word
158- } else if ( type == "triple" ) {
159- let line = new Range ( Pos ( start . line , 0 ) , clipPos ( doc , Pos ( start . line + 1 , 0 ) ) )
160- if ( cm . display . shift || doc . extend )
161- ourRange = extendRange ( doc , ourRange , line . anchor , line . head )
162- else
163- ourRange = line
164214 } else {
165- ourRange = extendRange ( doc , ourRange , start )
215+ let range = rangeForUnit ( cm , start , behavior . unit )
216+ if ( behavior . extend )
217+ ourRange = extendRange ( ourRange , range . anchor , range . head , behavior . extend )
218+ else
219+ ourRange = range
166220 }
167221
168- if ( ! addNew ) {
222+ if ( ! behavior . addNew ) {
169223 ourIndex = 0
170224 setSelection ( doc , new Selection ( [ ourRange ] , 0 ) , sel_mouse )
171225 startSel = doc . sel
172226 } else if ( ourIndex == - 1 ) {
173227 ourIndex = ranges . length
174228 setSelection ( doc , normalizeSelection ( ranges . concat ( [ ourRange ] ) , ourIndex ) ,
175229 { scroll : false , origin : "*mouse" } )
176- } else if ( ranges . length > 1 && ranges [ ourIndex ] . empty ( ) && type == "single " && ! e . shiftKey ) {
230+ } else if ( ranges . length > 1 && ranges [ ourIndex ] . empty ( ) && behavior . unit == "char " && ! behavior . extend ) {
177231 setSelection ( doc , normalizeSelection ( ranges . slice ( 0 , ourIndex ) . concat ( ranges . slice ( ourIndex + 1 ) ) , 0 ) ,
178232 { scroll : false , origin : "*mouse" } )
179233 startSel = doc . sel
@@ -186,7 +240,7 @@ function leftButtonSelect(cm, e, start, type, addNew) {
186240 if ( cmp ( lastPos , pos ) == 0 ) return
187241 lastPos = pos
188242
189- if ( type == "rect " ) {
243+ if ( behavior . unit == "rectangle " ) {
190244 let ranges = [ ] , tabSize = cm . options . tabSize
191245 let startCol = countColumn ( getLine ( doc , start . line ) . text , start . ch , tabSize )
192246 let posCol = countColumn ( getLine ( doc , pos . line ) . text , pos . ch , tabSize )
@@ -205,20 +259,14 @@ function leftButtonSelect(cm, e, start, type, addNew) {
205259 cm . scrollIntoView ( pos )
206260 } else {
207261 let oldRange = ourRange
208- let anchor = oldRange . anchor , head = pos
209- if ( type != "single" ) {
210- let range
211- if ( type == "double" )
212- range = cm . findWordAt ( pos )
213- else
214- range = new Range ( Pos ( pos . line , 0 ) , clipPos ( doc , Pos ( pos . line + 1 , 0 ) ) )
215- if ( cmp ( range . anchor , anchor ) > 0 ) {
216- head = range . head
217- anchor = minPos ( oldRange . from ( ) , range . anchor )
218- } else {
219- head = range . anchor
220- anchor = maxPos ( oldRange . to ( ) , range . head )
221- }
262+ let range = rangeForUnit ( cm , pos , behavior . unit )
263+ let anchor = oldRange . anchor , head
264+ if ( cmp ( range . anchor , anchor ) > 0 ) {
265+ head = range . head
266+ anchor = minPos ( oldRange . from ( ) , range . anchor )
267+ } else {
268+ head = range . anchor
269+ anchor = maxPos ( oldRange . to ( ) , range . head )
222270 }
223271 let ranges = startSel . ranges . slice ( 0 )
224272 ranges [ ourIndex ] = new Range ( clipPos ( doc , anchor ) , head )
@@ -235,7 +283,7 @@ function leftButtonSelect(cm, e, start, type, addNew) {
235283
236284 function extend ( e ) {
237285 let curCount = ++ counter
238- let cur = posFromMouse ( cm , e , true , type == "rect " )
286+ let cur = posFromMouse ( cm , e , true , behavior . unit == "rectangle " )
239287 if ( ! cur ) return
240288 if ( cmp ( cur , lastPos ) != 0 ) {
241289 cm . curOp . focus = activeElt ( )
0 commit comments