PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/Registers.java

#
Java | 739 lines | 547 code | 47 blank | 145 comment | 66 complexity | 4c42c64a8401c70982936c81f808467b MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * Registers.java - Register manager
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2003 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit;
  23. //{{{ Imports
  24. import java.awt.datatransfer.*;
  25. import java.awt.Toolkit;
  26. import java.io.*;
  27. import org.xml.sax.Attributes;
  28. import org.xml.sax.InputSource;
  29. import org.xml.sax.helpers.DefaultHandler;
  30. import org.gjt.sp.jedit.buffer.JEditBuffer;
  31. import org.gjt.sp.jedit.gui.*;
  32. import org.gjt.sp.jedit.msg.RegisterChanged;
  33. import org.gjt.sp.jedit.textarea.*;
  34. import org.gjt.sp.util.Log;
  35. //}}}
  36. /**
  37. * jEdit's registers are an extension of the clipboard metaphor.<p>
  38. *
  39. * A {@link Registers.Register} is string of text indexed by a
  40. * single character. Typically the text is taken from selected buffer text
  41. * and the index character is a keyboard character selected by the user.<p>
  42. *
  43. * This class defines a number of static methods
  44. * that give each register the properties of a virtual clipboard.<p>
  45. *
  46. * Two classes implement the {@link Registers.Register} interface. A
  47. * {@link Registers.ClipboardRegister} is tied to the contents of the
  48. * system clipboard. jEdit assigns a
  49. * {@link Registers.ClipboardRegister} to the register indexed under
  50. * the character <code>$</code>. A
  51. * {@link Registers.StringRegister} is created for registers assigned
  52. * by the user. In addition, jEdit assigns <code>%</code> to
  53. * the last text segment selected in the text area. On Windows this is a
  54. * {@link Registers.StringRegister}, on Unix under Java 2 version 1.4, a
  55. * {@link Registers.ClipboardRegister}.
  56. *
  57. * @author Slava Pestov
  58. * @author John Gellene (API documentation)
  59. * @version $Id: Registers.java 5443 2006-06-18 18:51:40Z vanza $
  60. */
  61. public class Registers
  62. {
  63. //{{{ copy() method
  64. /**
  65. * Copies the text selected in the text area into the specified register.
  66. * This will replace the existing contents of the designated register.
  67. *
  68. * @param textArea The text area
  69. * @param register The register
  70. * @since jEdit 2.7pre2
  71. */
  72. public static void copy(JEditTextArea textArea, char register)
  73. {
  74. String selection = textArea.getSelectedText();
  75. if(selection == null)
  76. return;
  77. setRegister(register,selection);
  78. HistoryModel.getModel("clipboard").addItem(selection);
  79. } //}}}
  80. //{{{ cut() method
  81. /**
  82. * Copies the text selected in the text area into the specified
  83. * register, and then removes it from the buffer.
  84. *
  85. * @param textArea The text area
  86. * @param register The register
  87. * @since jEdit 2.7pre2
  88. */
  89. public static void cut(JEditTextArea textArea, char register)
  90. {
  91. if(textArea.isEditable())
  92. {
  93. String selection = textArea.getSelectedText();
  94. if(selection == null)
  95. return;
  96. setRegister(register,selection);
  97. HistoryModel.getModel("clipboard").addItem(selection);
  98. textArea.setSelectedText("");
  99. }
  100. else
  101. textArea.getToolkit().beep();
  102. } //}}}
  103. //{{{ append() method
  104. /**
  105. * Appends the text selected in the text area to the specified register,
  106. * with a newline between the old and new text.
  107. * @param textArea The text area
  108. * @param register The register
  109. */
  110. public static void append(JEditTextArea textArea, char register)
  111. {
  112. append(textArea,register,"\n",false);
  113. } //}}}
  114. //{{{ append() method
  115. /**
  116. * Appends the text selected in the text area to the specified register.
  117. * @param textArea The text area
  118. * @param register The register
  119. * @param separator The separator to insert between the old and new text
  120. */
  121. public static void append(JEditTextArea textArea, char register,
  122. String separator)
  123. {
  124. append(textArea,register,separator,false);
  125. } //}}}
  126. //{{{ append() method
  127. /**
  128. * Appends the text selected in the text area to the specified register.
  129. * @param textArea The text area
  130. * @param register The register
  131. * @param separator The text to insert between the old and new text
  132. * @param cut Should the current selection be removed?
  133. * @since jEdit 3.2pre1
  134. */
  135. public static void append(JEditTextArea textArea, char register,
  136. String separator, boolean cut)
  137. {
  138. if(cut && !textArea.isEditable())
  139. {
  140. textArea.getToolkit().beep();
  141. return;
  142. }
  143. String selection = textArea.getSelectedText();
  144. if(selection == null)
  145. return;
  146. Register reg = getRegister(register);
  147. if(reg != null)
  148. {
  149. String registerContents = reg.toString();
  150. if(registerContents != null)
  151. {
  152. if(registerContents.endsWith(separator))
  153. selection = registerContents + selection;
  154. else
  155. selection = registerContents + separator + selection;
  156. }
  157. }
  158. setRegister(register,selection);
  159. HistoryModel.getModel("clipboard").addItem(selection);
  160. if(cut)
  161. textArea.setSelectedText("");
  162. } //}}}
  163. //{{{ paste() method
  164. /**
  165. * Insets the contents of the specified register into the text area.
  166. * @param textArea The text area
  167. * @param register The register
  168. * @since jEdit 2.7pre2
  169. */
  170. public static void paste(JEditTextArea textArea, char register)
  171. {
  172. paste(textArea,register,false);
  173. } //}}}
  174. //{{{ paste() method
  175. /**
  176. * Inserts the contents of the specified register into the text area.
  177. * @param textArea The text area
  178. * @param register The register
  179. * @param vertical Vertical (columnar) paste
  180. * @since jEdit 4.1pre1
  181. */
  182. public static void paste(JEditTextArea textArea, char register,
  183. boolean vertical)
  184. {
  185. if(!textArea.isEditable())
  186. {
  187. textArea.getToolkit().beep();
  188. return;
  189. }
  190. Register reg = getRegister(register);
  191. if(reg == null)
  192. {
  193. textArea.getToolkit().beep();
  194. return;
  195. }
  196. else
  197. {
  198. String selection = reg.toString();
  199. if(selection == null)
  200. {
  201. textArea.getToolkit().beep();
  202. return;
  203. }
  204. JEditBuffer buffer = textArea.getBuffer();
  205. try
  206. {
  207. buffer.beginCompoundEdit();
  208. /* vertical paste */
  209. if(vertical && textArea.getSelectionCount() == 0)
  210. {
  211. int caret = textArea.getCaretPosition();
  212. int caretLine = textArea.getCaretLine();
  213. Selection.Rect rect = new Selection.Rect(
  214. caretLine,caret,caretLine,caret);
  215. textArea.setSelectedText(rect,selection);
  216. caretLine = textArea.getCaretLine();
  217. if(caretLine != textArea.getLineCount() - 1)
  218. {
  219. int startColumn = rect.getStartColumn(
  220. buffer);
  221. int offset = buffer
  222. .getOffsetOfVirtualColumn(
  223. caretLine + 1,startColumn,null);
  224. if(offset == -1)
  225. {
  226. buffer.insertAtColumn(caretLine + 1,startColumn,"");
  227. textArea.setCaretPosition(
  228. buffer.getLineEndOffset(
  229. caretLine + 1) - 1);
  230. }
  231. else
  232. {
  233. textArea.setCaretPosition(
  234. buffer.getLineStartOffset(
  235. caretLine + 1) + offset);
  236. }
  237. }
  238. }
  239. else /* Regular paste */
  240. {
  241. textArea.replaceSelection(selection);
  242. }
  243. }
  244. finally {
  245. buffer.endCompoundEdit();
  246. }
  247. HistoryModel.getModel("clipboard").addItem(selection);
  248. }
  249. } //}}}
  250. //{{{ getRegister() method
  251. /**
  252. * Returns the specified register.
  253. * @param name The name
  254. */
  255. public static Register getRegister(char name)
  256. {
  257. if(name != '$' && name != '%')
  258. {
  259. if(!loaded)
  260. loadRegisters();
  261. }
  262. if(registers == null || name >= registers.length)
  263. return null;
  264. else
  265. return registers[name];
  266. } //}}}
  267. //{{{ setRegister() method
  268. /**
  269. * Sets the specified register.
  270. * @param name The name
  271. * @param newRegister The new value
  272. */
  273. public static void setRegister(char name, Register newRegister)
  274. {
  275. touchRegister(name);
  276. if(name >= registers.length)
  277. {
  278. Register[] newRegisters = new Register[
  279. Math.min(1<<16,name * 2)];
  280. System.arraycopy(registers,0,newRegisters,0,
  281. registers.length);
  282. registers = newRegisters;
  283. }
  284. registers[name] = newRegister;
  285. EditBus.send(new RegisterChanged(null,name));
  286. } //}}}
  287. //{{{ setRegister() method
  288. /**
  289. * Sets the specified register.
  290. * @param name The name
  291. * @param value The new value
  292. */
  293. public static void setRegister(char name, String value)
  294. {
  295. touchRegister(name);
  296. Register register = getRegister(name);
  297. if(register != null)
  298. {
  299. register.setValue(value);
  300. EditBus.send(new RegisterChanged(null,name));
  301. }
  302. else
  303. setRegister(name,new StringRegister(value));
  304. } //}}}
  305. //{{{ clearRegister() method
  306. /**
  307. * Sets the value of the specified register to <code>null</code>.
  308. * @param name The register name
  309. */
  310. public static void clearRegister(char name)
  311. {
  312. if(name >= registers.length)
  313. return;
  314. Register register = registers[name];
  315. if(name == '$' || name == '%')
  316. register.setValue("");
  317. else
  318. registers[name] = null;
  319. } //}}}
  320. //{{{ getRegisters() method
  321. /**
  322. * Returns an array of all available registers. Some of the elements
  323. * of this array might be <code>null</code>.
  324. */
  325. public static Register[] getRegisters()
  326. {
  327. if(!loaded)
  328. loadRegisters();
  329. return registers;
  330. } //}}}
  331. //{{{ getRegisterStatusPrompt() method
  332. /**
  333. * Returns the status prompt for the given register action. Only
  334. * intended to be called from <code>actions.xml</code>.
  335. * @since jEdit 4.2pre2
  336. */
  337. public static String getRegisterStatusPrompt(String action)
  338. {
  339. return jEdit.getProperty("view.status." + action,
  340. new String[] { getRegisterNameString() });
  341. } //}}}
  342. //{{{ getRegisterNameString() method
  343. /**
  344. * Returns a string of all defined registers, used by the status bar
  345. * (eg, "a b $ % ^").
  346. * @since jEdit 4.2pre2
  347. */
  348. public static String getRegisterNameString()
  349. {
  350. if(!loaded)
  351. loadRegisters();
  352. StringBuffer buf = new StringBuffer();
  353. for(int i = 0; i < registers.length; i++)
  354. {
  355. if(registers[i] != null)
  356. {
  357. if(buf.length() != 0)
  358. buf.append(' ');
  359. buf.append((char)i);
  360. }
  361. }
  362. if(buf.length() == 0)
  363. return jEdit.getProperty("view.status.no-registers");
  364. else
  365. return buf.toString();
  366. } //}}}
  367. //{{{ saveRegisters() method
  368. public static void saveRegisters()
  369. {
  370. if(!loaded || !modified)
  371. return;
  372. Log.log(Log.MESSAGE,Registers.class,"Saving registers.xml");
  373. File file1 = new File(MiscUtilities.constructPath(
  374. jEdit.getSettingsDirectory(), "#registers.xml#save#"));
  375. File file2 = new File(MiscUtilities.constructPath(
  376. jEdit.getSettingsDirectory(), "registers.xml"));
  377. if(file2.exists() && file2.lastModified() != registersModTime)
  378. {
  379. Log.log(Log.WARNING,Registers.class,file2 + " changed"
  380. + " on disk; will not save registers");
  381. return;
  382. }
  383. jEdit.backupSettingsFile(file2);
  384. String lineSep = System.getProperty("line.separator");
  385. BufferedWriter out = null;
  386. boolean ok = false;
  387. try
  388. {
  389. out = new BufferedWriter(new FileWriter(file1));
  390. out.write("<?xml version=\"1.0\"?>");
  391. out.write(lineSep);
  392. out.write("<!DOCTYPE REGISTERS SYSTEM \"registers.dtd\">");
  393. out.write(lineSep);
  394. out.write("<REGISTERS>");
  395. out.write(lineSep);
  396. Register[] registers = getRegisters();
  397. for(int i = 0; i < registers.length; i++)
  398. {
  399. Register register = registers[i];
  400. if(register == null ||
  401. i == '$' ||
  402. i == '%' ||
  403. register.toString().length() == 0
  404. )
  405. continue;
  406. out.write("<REGISTER NAME=\"");
  407. if(i == '"')
  408. out.write("&quot;");
  409. else
  410. out.write((char)i);
  411. out.write("\">");
  412. out.write(MiscUtilities.charsToEntities(
  413. register.toString()));
  414. out.write("</REGISTER>");
  415. out.write(lineSep);
  416. }
  417. out.write("</REGISTERS>");
  418. out.write(lineSep);
  419. ok = true;
  420. }
  421. catch(Exception e)
  422. {
  423. Log.log(Log.ERROR,Registers.class,e);
  424. }
  425. finally
  426. {
  427. try
  428. {
  429. if(out != null)
  430. out.close();
  431. }
  432. catch(IOException e)
  433. {
  434. }
  435. }
  436. if(ok)
  437. {
  438. /* to avoid data loss, only do this if the above
  439. * completed successfully */
  440. file2.delete();
  441. file1.renameTo(file2);
  442. }
  443. registersModTime = file2.lastModified();
  444. modified = false;
  445. } //}}}
  446. //{{{ Private members
  447. private static Register[] registers;
  448. private static long registersModTime;
  449. private static boolean loaded, loading, modified;
  450. private Registers() {}
  451. static
  452. {
  453. registers = new Register[256];
  454. Toolkit toolkit = Toolkit.getDefaultToolkit();
  455. registers['$'] = new ClipboardRegister(
  456. toolkit.getSystemClipboard());
  457. Clipboard selection = toolkit.getSystemSelection();
  458. if(selection != null)
  459. registers['%'] = new ClipboardRegister(selection);
  460. }
  461. //{{{ touchRegister() method
  462. private static void touchRegister(char name)
  463. {
  464. if(name == '%' || name == '$')
  465. return;
  466. if(!loaded)
  467. loadRegisters();
  468. if(!loading)
  469. modified = true;
  470. } //}}}
  471. //{{{ loadRegisters() method
  472. private static void loadRegisters()
  473. {
  474. loaded = true;
  475. String settingsDirectory = jEdit.getSettingsDirectory();
  476. if(settingsDirectory == null)
  477. return;
  478. File registerFile = new File(MiscUtilities.constructPath(
  479. jEdit.getSettingsDirectory(),"registers.xml"));
  480. if(!registerFile.exists())
  481. return;
  482. registersModTime = registerFile.lastModified();
  483. Log.log(Log.MESSAGE,jEdit.class,"Loading registers.xml");
  484. RegistersHandler handler = new RegistersHandler();
  485. try {
  486. loading = true;
  487. MiscUtilities.parseXML(new FileInputStream(registerFile),
  488. handler);
  489. }
  490. catch (IOException ioe) {
  491. Log.log(Log.ERROR, Registers.class, ioe);
  492. } finally {
  493. loading = false;
  494. }
  495. } //}}}
  496. //}}}
  497. //{{{ Inner classes
  498. //{{{ Register interface
  499. /**
  500. * A register.
  501. */
  502. public interface Register
  503. {
  504. /**
  505. * Converts to a string.
  506. */
  507. String toString();
  508. /**
  509. * Sets the register contents.
  510. */
  511. void setValue(String value);
  512. } //}}}
  513. //{{{ ClipboardRegister class
  514. /**
  515. * A clipboard register. Register "$" should always be an
  516. * instance of this.
  517. */
  518. public static class ClipboardRegister implements Register
  519. {
  520. Clipboard clipboard;
  521. public ClipboardRegister(Clipboard clipboard)
  522. {
  523. this.clipboard = clipboard;
  524. }
  525. /**
  526. * Sets the clipboard contents.
  527. */
  528. public void setValue(String value)
  529. {
  530. StringSelection selection = new StringSelection(value);
  531. clipboard.setContents(selection,null);
  532. }
  533. /**
  534. * Returns the clipboard contents.
  535. */
  536. public String toString()
  537. {
  538. try
  539. {
  540. String selection = (String)(clipboard
  541. .getContents(this).getTransferData(
  542. DataFlavor.stringFlavor));
  543. boolean trailingEOL = (selection.endsWith("\n")
  544. || selection.endsWith(System.getProperty(
  545. "line.separator")));
  546. // Some Java versions return the clipboard
  547. // contents using the native line separator,
  548. // so have to convert it here
  549. BufferedReader in = new BufferedReader(
  550. new StringReader(selection));
  551. StringBuffer buf = new StringBuffer();
  552. String line;
  553. while((line = in.readLine()) != null)
  554. {
  555. // broken Eclipse workaround!
  556. // 24 Febuary 2004
  557. if(line.endsWith("\0"))
  558. {
  559. line = line.substring(0,
  560. line.length() - 1);
  561. }
  562. buf.append(line);
  563. buf.append('\n');
  564. }
  565. // remove trailing \n
  566. if(!trailingEOL && buf.length() != 0)
  567. buf.setLength(buf.length() - 1);
  568. return buf.toString();
  569. }
  570. catch(Exception e)
  571. {
  572. Log.log(Log.NOTICE,this,e);
  573. return null;
  574. }
  575. }
  576. } //}}}
  577. //{{{ StringRegister class
  578. /**
  579. * Register that stores a string.
  580. */
  581. public static class StringRegister implements Register
  582. {
  583. private String value;
  584. /**
  585. * Creates a new string register.
  586. * @param value The contents
  587. */
  588. public StringRegister(String value)
  589. {
  590. this.value = value;
  591. }
  592. /**
  593. * Sets the register contents.
  594. */
  595. public void setValue(String value)
  596. {
  597. this.value = value;
  598. }
  599. /**
  600. * Converts to a string.
  601. */
  602. public String toString()
  603. {
  604. return value;
  605. }
  606. /**
  607. * Called when this register is no longer available. This
  608. * implementation does nothing.
  609. */
  610. public void dispose() {}
  611. } //}}}
  612. //{{{ RegistersHandler class
  613. static class RegistersHandler extends DefaultHandler
  614. {
  615. //{{{ resolveEntity() method
  616. public InputSource resolveEntity(String publicId, String systemId)
  617. {
  618. return MiscUtilities.findEntity(systemId, "registers.dtd", getClass());
  619. } //}}}
  620. //{{{ startElement() method
  621. public void startElement(String uri, String localName,
  622. String qName, Attributes attrs)
  623. {
  624. registerName = attrs.getValue("NAME");
  625. inRegister = "REGISTER".equals(qName);
  626. } //}}}
  627. //{{{ endElement() method
  628. public void endElement(String uri, String localName, String name)
  629. {
  630. if(name.equals("REGISTER"))
  631. {
  632. if(registerName == null || registerName.length() != 1)
  633. Log.log(Log.ERROR,this,"Malformed NAME: " + registerName);
  634. else
  635. setRegister(registerName.charAt(0),charData.toString());
  636. inRegister = false;
  637. charData.setLength(0);
  638. }
  639. } //}}}
  640. //{{{ characters() method
  641. public void characters(char[] ch, int start, int length)
  642. {
  643. if (inRegister)
  644. charData.append(ch, start, length);
  645. } //}}}
  646. //{{{ Private members
  647. private String registerName;
  648. private StringBuffer charData = new StringBuffer();
  649. private boolean inRegister;
  650. //}}}
  651. } //}}}
  652. //}}}
  653. }