PageRenderTime 42ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 741 lines | 551 code | 46 blank | 144 comment | 73 complexity | 1dc120fcb59af0117444f3727c0c79df 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 4835 2003-07-23 06:08:54Z 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. try
  381. {
  382. BufferedWriter out = new BufferedWriter(
  383. new FileWriter(file1));
  384. out.write("<?xml version=\"1.0\"?>");
  385. out.write(lineSep);
  386. out.write("<!DOCTYPE REGISTERS SYSTEM \"registers.dtd\">");
  387. out.write(lineSep);
  388. out.write("<REGISTERS>");
  389. out.write(lineSep);
  390. Register[] registers = getRegisters();
  391. for(int i = 0; i < registers.length; i++)
  392. {
  393. Register register = registers[i];
  394. if(register == null || i == '$' || i == '%')
  395. continue;
  396. out.write("<REGISTER NAME=\"");
  397. if(i == '"')
  398. out.write("&quot;");
  399. else
  400. out.write((char)i);
  401. out.write("\">");
  402. out.write(MiscUtilities.charsToEntities(
  403. register.toString()));
  404. out.write("</REGISTER>");
  405. out.write(lineSep);
  406. }
  407. out.write("</REGISTERS>");
  408. out.write(lineSep);
  409. out.close();
  410. /* to avoid data loss, only do this if the above
  411. * completed successfully */
  412. file2.delete();
  413. file1.renameTo(file2);
  414. }
  415. catch(Exception e)
  416. {
  417. Log.log(Log.ERROR,Registers.class,e);
  418. }
  419. registersModTime = file2.lastModified();
  420. modified = false;
  421. } //}}}
  422. //{{{ Private members
  423. private static Register[] registers;
  424. private static long registersModTime;
  425. private static boolean loaded, loading, modified;
  426. private Registers() {}
  427. static
  428. {
  429. registers = new Register[256];
  430. registers['$'] = new ClipboardRegister(Toolkit
  431. .getDefaultToolkit().getSystemClipboard());
  432. }
  433. //{{{ loadRegisters() method
  434. private static void loadRegisters()
  435. {
  436. String settingsDirectory = jEdit.getSettingsDirectory();
  437. if(settingsDirectory == null)
  438. return;
  439. File registerFile = new File(MiscUtilities.constructPath(
  440. jEdit.getSettingsDirectory(),"registers.xml"));
  441. if(!registerFile.exists())
  442. return;
  443. registersModTime = registerFile.lastModified();
  444. loaded = true;
  445. Log.log(Log.MESSAGE,jEdit.class,"Loading registers.xml");
  446. RegistersHandler handler = new RegistersHandler();
  447. XmlParser parser = new XmlParser();
  448. parser.setHandler(handler);
  449. Reader in = null;
  450. try
  451. {
  452. loading = true;
  453. in = new BufferedReader(new FileReader(registerFile));
  454. parser.parse(null, null, in);
  455. }
  456. catch(XmlException xe)
  457. {
  458. int line = xe.getLine();
  459. String message = xe.getMessage();
  460. Log.log(Log.ERROR,Registers.class,registerFile + ":"
  461. + line + ": " + message);
  462. }
  463. catch(FileNotFoundException fnf)
  464. {
  465. //Log.log(Log.DEBUG,Registers.class,fnf);
  466. }
  467. catch(Exception e)
  468. {
  469. Log.log(Log.ERROR,Registers.class,e);
  470. }
  471. finally
  472. {
  473. loading = false;
  474. try
  475. {
  476. if(in != null)
  477. in.close();
  478. }
  479. catch(IOException io)
  480. {
  481. Log.log(Log.ERROR,Registers.class,io);
  482. }
  483. }
  484. } //}}}
  485. //}}}
  486. //{{{ Inner classes
  487. //{{{ Register interface
  488. /**
  489. * A register.
  490. */
  491. public interface Register
  492. {
  493. /**
  494. * Converts to a string.
  495. */
  496. String toString();
  497. /**
  498. * Sets the register contents.
  499. */
  500. void setValue(String value);
  501. } //}}}
  502. //{{{ ClipboardRegister class
  503. /**
  504. * A clipboard register. Register "$" should always be an
  505. * instance of this.
  506. */
  507. public static class ClipboardRegister implements Register
  508. {
  509. Clipboard clipboard;
  510. public ClipboardRegister(Clipboard clipboard)
  511. {
  512. this.clipboard = clipboard;
  513. }
  514. /**
  515. * Sets the clipboard contents.
  516. */
  517. public void setValue(String value)
  518. {
  519. StringSelection selection = new StringSelection(value);
  520. clipboard.setContents(selection,null);
  521. }
  522. /**
  523. * Returns the clipboard contents.
  524. */
  525. public String toString()
  526. {
  527. try
  528. {
  529. String selection = (String)(clipboard
  530. .getContents(this).getTransferData(
  531. DataFlavor.stringFlavor));
  532. boolean trailingEOL = (selection.endsWith("\n")
  533. || selection.endsWith(System.getProperty(
  534. "line.separator")));
  535. // Some Java versions return the clipboard
  536. // contents using the native line separator,
  537. // so have to convert it here
  538. BufferedReader in = new BufferedReader(
  539. new StringReader(selection));
  540. StringBuffer buf = new StringBuffer();
  541. String line;
  542. while((line = in.readLine()) != null)
  543. {
  544. buf.append(line);
  545. buf.append('\n');
  546. }
  547. // remove trailing \n
  548. if(!trailingEOL && buf.length() != 0)
  549. buf.setLength(buf.length() - 1);
  550. return buf.toString();
  551. }
  552. catch(Exception e)
  553. {
  554. Log.log(Log.NOTICE,this,e);
  555. return null;
  556. }
  557. }
  558. } //}}}
  559. //{{{ StringRegister class
  560. /**
  561. * Register that stores a string.
  562. */
  563. public static class StringRegister implements Register
  564. {
  565. private String value;
  566. /**
  567. * Creates a new string register.
  568. * @param value The contents
  569. */
  570. public StringRegister(String value)
  571. {
  572. this.value = value;
  573. }
  574. /**
  575. * Sets the register contents.
  576. */
  577. public void setValue(String value)
  578. {
  579. this.value = value;
  580. }
  581. /**
  582. * Converts to a string.
  583. */
  584. public String toString()
  585. {
  586. return value;
  587. }
  588. /**
  589. * Called when this register is no longer available. This
  590. * implementation does nothing.
  591. */
  592. public void dispose() {}
  593. } //}}}
  594. //{{{ RegistersHandler class
  595. static class RegistersHandler extends HandlerBase
  596. {
  597. //{{{ resolveEntity() method
  598. public Object resolveEntity(String publicId, String systemId)
  599. {
  600. if("registers.dtd".equals(systemId))
  601. {
  602. // this will result in a slight speed up, since we
  603. // don't need to read the DTD anyway, as AElfred is
  604. // non-validating
  605. return new StringReader("<!-- -->");
  606. /* try
  607. {
  608. return new BufferedReader(new InputStreamReader(
  609. getClass().getResourceAsStream("registers.dtd")));
  610. }
  611. catch(Exception e)
  612. {
  613. Log.log(Log.ERROR,this,"Error while opening"
  614. + " recent.dtd:");
  615. Log.log(Log.ERROR,this,e);
  616. } */
  617. }
  618. return null;
  619. } //}}}
  620. //{{{ attribute() method
  621. public void attribute(String aname, String value, boolean isSpecified)
  622. {
  623. if(aname.equals("NAME"))
  624. registerName = value;
  625. } //}}}
  626. //{{{ doctypeDecl() method
  627. public void doctypeDecl(String name, String publicId,
  628. String systemId) throws Exception
  629. {
  630. if("REGISTERS".equals(name))
  631. return;
  632. Log.log(Log.ERROR,this,"registers.xml: DOCTYPE must be REGISTERS");
  633. } //}}}
  634. //{{{ endElement() method
  635. public void endElement(String name)
  636. {
  637. if(name.equals("REGISTER"))
  638. {
  639. if(registerName == null || registerName.length() != 1)
  640. Log.log(Log.ERROR,this,"Malformed NAME: " + registerName);
  641. else
  642. setRegister(registerName.charAt(0),charData);
  643. }
  644. } //}}}
  645. //{{{ charData() method
  646. public void charData(char[] ch, int start, int length)
  647. {
  648. charData = new String(ch,start,length);
  649. } //}}}
  650. //{{{ Private members
  651. private String registerName;
  652. private String charData;
  653. //}}}
  654. } //}}}
  655. //}}}
  656. }