/jEdit/tags/before-screen-line-refactoring/org/gjt/sp/jedit/textarea/MouseHandler.java

# · Java · 562 lines · 435 code · 76 blank · 51 comment · 114 complexity · 2bed1cf796ebb55c55b39ef53c2a513a MD5 · raw file

  1. /*
  2. * JEditTextArea.java - jEdit's text component
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2004 Slava Pestov
  7. * Portions copyright (C) 2000 Ollie Rutherfurd
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.textarea;
  24. //{{{ Imports
  25. import java.awt.*;
  26. import java.awt.event.*;
  27. import java.lang.reflect.Method;
  28. import java.util.Iterator;
  29. import java.util.LinkedList;
  30. import java.util.List;
  31. import java.util.Set;
  32. import java.util.TooManyListenersException;
  33. import java.util.TreeSet;
  34. import java.util.Vector;
  35. import javax.swing.*;
  36. import javax.swing.border.*;
  37. import javax.swing.event.*;
  38. import javax.swing.plaf.metal.MetalLookAndFeel;
  39. import javax.swing.text.Segment;
  40. import org.gjt.sp.jedit.*;
  41. import org.gjt.sp.jedit.buffer.*;
  42. import org.gjt.sp.jedit.gui.*;
  43. import org.gjt.sp.jedit.syntax.*;
  44. import org.gjt.sp.util.Log;
  45. //}}}
  46. class MouseHandler extends MouseInputAdapter
  47. {
  48. //{{{ MouseHandler constructor
  49. MouseHandler(JEditTextArea textArea)
  50. {
  51. this.textArea = textArea;
  52. } //}}}
  53. //{{{ mousePressed() method
  54. public void mousePressed(MouseEvent evt)
  55. {
  56. control = (OperatingSystem.isMacOS() && evt.isMetaDown())
  57. || (!OperatingSystem.isMacOS() && evt.isControlDown());
  58. // so that Home <mouse click> Home is not the same
  59. // as pressing Home twice in a row
  60. textArea.getInputHandler().resetLastActionCount();
  61. quickCopyDrag = (textArea.isQuickCopyEnabled() &&
  62. GUIUtilities.isMiddleButton(evt.getModifiers()));
  63. if(!quickCopyDrag)
  64. {
  65. textArea.requestFocus();
  66. JEditTextArea.focusedComponent = textArea;
  67. }
  68. if(!textArea.getBuffer().isLoaded())
  69. return;
  70. int x = evt.getX();
  71. int y = evt.getY();
  72. dragStart = textArea.xyToOffset(x,y,
  73. !(textArea.getPainter().isBlockCaretEnabled()
  74. || textArea.isOverwriteEnabled()));
  75. dragStartLine = textArea.getLineOfOffset(dragStart);
  76. dragStartOffset = dragStart - textArea.getLineStartOffset(
  77. dragStartLine);
  78. if(GUIUtilities.isPopupTrigger(evt)
  79. && textArea.getRightClickPopup() != null)
  80. {
  81. if(textArea.isRightClickPopupEnabled())
  82. textArea.handlePopupTrigger(evt);
  83. return;
  84. }
  85. dragged = false;
  86. textArea.blink = true;
  87. textArea.invalidateLine(textArea.getCaretLine());
  88. clickCount = evt.getClickCount();
  89. if(textArea.isDragEnabled()
  90. && textArea.insideSelection(x,y)
  91. && clickCount == 1 && !evt.isShiftDown())
  92. {
  93. maybeDragAndDrop = true;
  94. textArea.moveCaretPosition(dragStart,false);
  95. return;
  96. }
  97. else
  98. maybeDragAndDrop = false;
  99. switch(clickCount)
  100. {
  101. case 1:
  102. doSingleClick(evt);
  103. break;
  104. case 2:
  105. doDoubleClick();
  106. break;
  107. default: //case 3:
  108. doTripleClick();
  109. break;
  110. }
  111. } //}}}
  112. //{{{ doSingleClick() method
  113. private void doSingleClick(MouseEvent evt)
  114. {
  115. /* if(buffer.insideCompoundEdit())
  116. buffer.endCompoundEdit(); */
  117. int x = evt.getX();
  118. int extraEndVirt = 0;
  119. if(textArea.chunkCache.getLineInfo(
  120. textArea.getLastScreenLine()).lastSubregion)
  121. {
  122. float dragStartLineWidth = textArea.offsetToXY(
  123. dragStartLine,
  124. textArea.getLineLength(dragStartLine)).x;
  125. if(x > dragStartLineWidth)
  126. {
  127. extraEndVirt = (int)(
  128. (x - dragStartLineWidth)
  129. / textArea.charWidth);
  130. if(!textArea.getPainter().isBlockCaretEnabled()
  131. && !textArea.isOverwriteEnabled()
  132. && (x - textArea.getHorizontalOffset())
  133. % textArea.charWidth > textArea.charWidth / 2)
  134. {
  135. extraEndVirt++;
  136. }
  137. }
  138. }
  139. if(control || textArea.isRectangularSelectionEnabled())
  140. {
  141. int screenLine = (evt.getY() / textArea.getPainter()
  142. .getFontMetrics().getHeight());
  143. if(screenLine > textArea.getLastScreenLine())
  144. screenLine = textArea.getLastScreenLine();
  145. ChunkCache.LineInfo info = textArea.chunkCache.getLineInfo(screenLine);
  146. if(info.lastSubregion && extraEndVirt != 0)
  147. {
  148. if(!textArea.isEditable())
  149. {
  150. textArea.getToolkit().beep();
  151. return;
  152. }
  153. // control-click in virtual space inserts
  154. // whitespace and moves caret
  155. String whitespace = MiscUtilities
  156. .createWhiteSpace(extraEndVirt,0);
  157. textArea.getBuffer().insert(dragStart,whitespace);
  158. dragStart += whitespace.length();
  159. }
  160. }
  161. if(evt.isShiftDown())
  162. {
  163. // XXX: getMarkPosition() deprecated!
  164. textArea.resizeSelection(
  165. textArea.getMarkPosition(),dragStart,extraEndVirt,
  166. textArea.isRectangularSelectionEnabled()
  167. || control);
  168. if(!quickCopyDrag)
  169. textArea.moveCaretPosition(dragStart,false);
  170. // so that shift-click-drag works
  171. dragStartLine = textArea.getMarkLine();
  172. dragStart = textArea.getMarkPosition();
  173. dragStartOffset = dragStart
  174. - textArea.getLineStartOffset(dragStartLine);
  175. // so that quick copy works
  176. dragged = true;
  177. return;
  178. }
  179. if(!quickCopyDrag)
  180. textArea.moveCaretPosition(dragStart,false);
  181. if(!(textArea.isMultipleSelectionEnabled()
  182. || quickCopyDrag))
  183. textArea.selectNone();
  184. } //}}}
  185. //{{{ doDoubleClick() method
  186. private void doDoubleClick()
  187. {
  188. // Ignore empty lines
  189. if(textArea.getLineLength(dragStartLine) == 0)
  190. return;
  191. String lineText = textArea.getLineText(dragStartLine);
  192. String noWordSep = textArea.getBuffer()
  193. .getStringProperty("noWordSep");
  194. if(dragStartOffset == textArea.getLineLength(dragStartLine))
  195. dragStartOffset--;
  196. boolean joinNonWordChars =
  197. jEdit.getBooleanProperty("view.joinNonWordChars");
  198. int wordStart = TextUtilities.findWordStart(lineText,
  199. dragStartOffset,noWordSep,joinNonWordChars);
  200. int wordEnd = TextUtilities.findWordEnd(lineText,
  201. dragStartOffset+1,noWordSep,joinNonWordChars);
  202. int lineStart = textArea.getLineStartOffset(dragStartLine);
  203. Selection sel = new Selection.Range(
  204. lineStart + wordStart,
  205. lineStart + wordEnd);
  206. if(textArea.isMultipleSelectionEnabled())
  207. textArea.addToSelection(sel);
  208. else
  209. textArea.setSelection(sel);
  210. if(quickCopyDrag)
  211. quickCopyDrag = false;
  212. textArea.moveCaretPosition(lineStart + wordEnd,false);
  213. dragged = true;
  214. } //}}}
  215. //{{{ doTripleClick() method
  216. private void doTripleClick()
  217. {
  218. int newCaret = textArea.getLineEndOffset(dragStartLine);
  219. if(dragStartLine == textArea.getLineCount() - 1)
  220. newCaret--;
  221. Selection sel = new Selection.Range(
  222. textArea.getLineStartOffset(dragStartLine),
  223. newCaret);
  224. if(textArea.isMultipleSelectionEnabled())
  225. textArea.addToSelection(sel);
  226. else
  227. textArea.setSelection(sel);
  228. if(quickCopyDrag)
  229. quickCopyDrag = false;
  230. textArea.moveCaretPosition(newCaret,false);
  231. dragged = true;
  232. } //}}}
  233. //{{{ mouseDragged() method
  234. public void mouseDragged(MouseEvent evt)
  235. {
  236. if(maybeDragAndDrop)
  237. {
  238. textArea.startDragAndDrop(evt,control);
  239. return;
  240. }
  241. if(textArea.isDragInProgress())
  242. return;
  243. JPopupMenu popup = textArea.getRightClickPopup();
  244. if(GUIUtilities.isPopupTrigger(evt)
  245. || (popup != null && popup.isVisible()))
  246. return;
  247. if(!textArea.getBuffer().isLoaded())
  248. return;
  249. TextAreaPainter painter = textArea.getPainter();
  250. if(evt.getY() < 0)
  251. {
  252. int delta = Math.min(-1,evt.getY()
  253. / painter.getFontMetrics()
  254. .getHeight());
  255. textArea.setFirstLine(textArea.getFirstLine() + delta);
  256. }
  257. else if(evt.getY() >= painter.getHeight())
  258. {
  259. int delta = Math.max(1,(evt.getY()
  260. - painter.getHeight()) /
  261. painter.getFontMetrics()
  262. .getHeight());
  263. if(textArea.lastLinePartial)
  264. delta--;
  265. textArea.setFirstLine(textArea.getFirstLine() + delta);
  266. }
  267. if(quickCopyDrag)
  268. {
  269. textArea.setStatusMessage(jEdit.getProperty(
  270. "view.status.rect-quick-copy"));
  271. clearStatus = true;
  272. }
  273. switch(clickCount)
  274. {
  275. case 1:
  276. doSingleDrag(evt);
  277. break;
  278. case 2:
  279. doDoubleDrag(evt);
  280. break;
  281. default: //case 3:
  282. doTripleDrag(evt);
  283. break;
  284. }
  285. } //}}}
  286. //{{{ doSingleDrag() method
  287. private void doSingleDrag(MouseEvent evt)
  288. {
  289. dragged = true;
  290. TextAreaPainter painter = textArea.getPainter();
  291. int x = evt.getX();
  292. int y = evt.getY();
  293. if(y < 0)
  294. y = 0;
  295. else if(y >= painter.getHeight())
  296. y = painter.getHeight() - 1;
  297. int dot = textArea.xyToOffset(x,y,
  298. (!painter.isBlockCaretEnabled()
  299. && !textArea.isOverwriteEnabled())
  300. || quickCopyDrag);
  301. int dotLine = textArea.getLineOfOffset(dot);
  302. int extraEndVirt = 0;
  303. if(textArea.chunkCache.getLineInfo(
  304. textArea.getLastScreenLine())
  305. .lastSubregion)
  306. {
  307. float dotLineWidth = textArea.offsetToXY(
  308. dotLine,textArea.getLineLength(dotLine)).x;
  309. if(x > dotLineWidth)
  310. {
  311. extraEndVirt = (int)((x - dotLineWidth) / textArea.charWidth);
  312. if(!painter.isBlockCaretEnabled()
  313. && !textArea.isOverwriteEnabled()
  314. && (x - textArea.getHorizontalOffset()) % textArea.charWidth > textArea.charWidth / 2)
  315. extraEndVirt++;
  316. }
  317. }
  318. textArea.resizeSelection(dragStart,dot,extraEndVirt,
  319. textArea.isRectangularSelectionEnabled()
  320. || control);
  321. if(quickCopyDrag)
  322. {
  323. // just scroll to the dragged location
  324. textArea.scrollTo(dotLine,dot - textArea.getLineStartOffset(dotLine),false);
  325. }
  326. else
  327. {
  328. if(dot != textArea.getCaretPosition())
  329. textArea.moveCaretPosition(dot,false);
  330. if(textArea.isRectangularSelectionEnabled()
  331. && extraEndVirt != 0)
  332. {
  333. textArea.scrollTo(dotLine,dot - textArea.getLineStartOffset(dotLine)
  334. + extraEndVirt,false);
  335. }
  336. }
  337. } //}}}
  338. //{{{ doDoubleDrag() method
  339. private void doDoubleDrag(MouseEvent evt)
  340. {
  341. int markLineStart = textArea.getLineStartOffset(dragStartLine);
  342. int markLineLength = textArea.getLineLength(dragStartLine);
  343. int mark = dragStartOffset;
  344. TextAreaPainter painter = textArea.getPainter();
  345. int pos = textArea.xyToOffset(evt.getX(),
  346. Math.max(0,Math.min(painter.getHeight(),evt.getY())),
  347. !(painter.isBlockCaretEnabled()
  348. || textArea.isOverwriteEnabled()));
  349. int line = textArea.getLineOfOffset(pos);
  350. int lineStart = textArea.getLineStartOffset(line);
  351. int lineLength = textArea.getLineLength(line);
  352. int offset = pos - lineStart;
  353. String lineText = textArea.getLineText(line);
  354. String markLineText = textArea.getLineText(dragStartLine);
  355. String noWordSep = textArea.getBuffer()
  356. .getStringProperty("noWordSep");
  357. boolean joinNonWordChars =
  358. jEdit.getBooleanProperty("view.joinNonWordChars");
  359. if(markLineStart + dragStartOffset > lineStart + offset)
  360. {
  361. if(offset != 0 && offset != lineLength)
  362. {
  363. offset = TextUtilities.findWordStart(
  364. lineText,offset,noWordSep,
  365. joinNonWordChars);
  366. }
  367. if(markLineLength != 0)
  368. {
  369. mark = TextUtilities.findWordEnd(
  370. markLineText,mark,noWordSep,
  371. joinNonWordChars);
  372. }
  373. }
  374. else
  375. {
  376. if(offset != 0 && lineLength != 0)
  377. {
  378. offset = TextUtilities.findWordEnd(
  379. lineText,offset,noWordSep,
  380. joinNonWordChars);
  381. }
  382. if(mark != 0 && mark != markLineLength)
  383. {
  384. mark = TextUtilities.findWordStart(
  385. markLineText,mark,noWordSep,
  386. joinNonWordChars);
  387. }
  388. }
  389. if(lineStart + offset == textArea.getCaretPosition())
  390. return;
  391. textArea.resizeSelection(markLineStart + mark,
  392. lineStart + offset,0,false);
  393. textArea.moveCaretPosition(lineStart + offset,false);
  394. dragged = true;
  395. } //}}}
  396. //{{{ doTripleDrag() method
  397. private void doTripleDrag(MouseEvent evt)
  398. {
  399. TextAreaPainter painter = textArea.getPainter();
  400. int offset = textArea.xyToOffset(evt.getX(),
  401. Math.max(0,Math.min(painter.getHeight(),evt.getY())),
  402. false);
  403. int mouseLine = textArea.getLineOfOffset(offset);
  404. int mark;
  405. int mouse;
  406. if(dragStartLine > mouseLine)
  407. {
  408. mark = textArea.getLineEndOffset(dragStartLine) - 1;
  409. if(offset == textArea.getLineEndOffset(mouseLine) - 1)
  410. mouse = offset;
  411. else
  412. mouse = textArea.getLineStartOffset(mouseLine);
  413. }
  414. else
  415. {
  416. mark = textArea.getLineStartOffset(dragStartLine);
  417. if(offset == textArea.getLineStartOffset(mouseLine))
  418. mouse = offset;
  419. else if(offset == textArea.getLineEndOffset(mouseLine) - 1
  420. && mouseLine != textArea.getLineCount() - 1)
  421. mouse = textArea.getLineEndOffset(mouseLine);
  422. else
  423. mouse = textArea.getLineEndOffset(mouseLine) - 1;
  424. }
  425. mouse = Math.min(textArea.getBuffer().getLength(),mouse);
  426. if(mouse == textArea.getCaretPosition())
  427. return;
  428. textArea.resizeSelection(mark,mouse,0,false);
  429. textArea.moveCaretPosition(mouse,false);
  430. dragged = true;
  431. } //}}}
  432. //{{{ mouseReleased() method
  433. public void mouseReleased(MouseEvent evt)
  434. {
  435. // middle mouse button drag inserts selection
  436. // at caret position
  437. Selection sel = textArea.getSelectionAtOffset(dragStart);
  438. if(dragged && sel != null)
  439. {
  440. Registers.setRegister('%',textArea.getSelectedText(sel));
  441. if(quickCopyDrag)
  442. {
  443. textArea.removeFromSelection(sel);
  444. Registers.paste(JEditTextArea.focusedComponent,
  445. '%',sel instanceof Selection.Rect);
  446. JEditTextArea.focusedComponent.requestFocus();
  447. }
  448. }
  449. else if(!dragged && textArea.isQuickCopyEnabled() &&
  450. GUIUtilities.isMiddleButton(evt.getModifiers()))
  451. {
  452. textArea.requestFocus();
  453. JEditTextArea.focusedComponent = textArea;
  454. textArea.setCaretPosition(dragStart,false);
  455. if(!textArea.isEditable())
  456. textArea.getToolkit().beep();
  457. else
  458. Registers.paste(textArea,'%',control);
  459. }
  460. else if(maybeDragAndDrop
  461. && !textArea.isMultipleSelectionEnabled())
  462. {
  463. textArea.selectNone();
  464. }
  465. dragged = false;
  466. if(clearStatus)
  467. {
  468. clearStatus = false;
  469. textArea.setStatusMessage(null);
  470. }
  471. } //}}}
  472. //{{{ Private members
  473. private JEditTextArea textArea;
  474. private int dragStartLine;
  475. private int dragStartOffset;
  476. private int dragStart;
  477. private int clickCount;
  478. private boolean dragged;
  479. private boolean quickCopyDrag;
  480. private boolean clearStatus;
  481. private boolean control;
  482. /* with drag and drop on, a mouse down in a selection does not
  483. immediately deselect */
  484. private boolean maybeDragAndDrop;
  485. //}}}
  486. }