PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/Registers.java

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