Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/Registers.java


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