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

Quick Search    Search Deep

Source code: com/virtuosotechnologies/asaph/modelutils/DataTransferUtilsImpl.java


1   /*
2   ================================================================================
3   
4     FILE:  DataTransferUtilsImpl.java
5     
6     PROJECT:
7     
8       Asaph
9     
10    CONTENTS:
11    
12      Implementation of DataTransferUtils API
13    
14    PROGRAMMERS:
15    
16      Daniel Azuma (DA)  <dazuma@kagi.com>
17    
18    COPYRIGHT:
19    
20      Copyright (C) 2003  Daniel Azuma  (dazuma@kagi.com)
21      
22      This program is free software; you can redistribute it and/or
23      modify it under the terms of the GNU General Public License as
24      published by the Free Software Foundation; either version 2
25      of the License, or (at your option) any later version.
26      
27      This program is distributed in the hope that it will be useful,
28      but WITHOUT ANY WARRANTY; without even the implied warranty of
29      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30      GNU General Public License for more details.
31      
32      You should have received a copy of the GNU General Public
33      License along with this program; if not, write to
34        Free Software Foundation, Inc.
35        59 Temple Place, Suite 330
36        Boston, MA 02111-1307 USA
37  
38  ================================================================================
39  */
40  
41  
42  package com.virtuosotechnologies.asaph.modelutils;
43  
44  
45  import java.awt.Toolkit;
46  import java.awt.datatransfer.Transferable;
47  import java.awt.datatransfer.DataFlavor;
48  import java.awt.datatransfer.UnsupportedFlavorException;
49  import java.awt.datatransfer.Clipboard;
50  import java.awt.datatransfer.ClipboardOwner;
51  import java.util.Collection;
52  import java.util.Iterator;
53  import java.io.IOException;
54  import javax.swing.event.UndoableEditListener;
55  import javax.swing.event.UndoableEditEvent;
56  
57  import com.virtuosotechnologies.lib.util.CollectingCompoundEdit;
58  
59  import com.virtuosotechnologies.asaph.notationmanager.NotationManager;
60  import com.virtuosotechnologies.asaph.model.SongLine;
61  import com.virtuosotechnologies.asaph.model.StringSongLineMember;
62  import com.virtuosotechnologies.asaph.model.ChordSet;
63  import com.virtuosotechnologies.asaph.model.Song;
64  import com.virtuosotechnologies.asaph.model.Variation;
65  import com.virtuosotechnologies.asaph.model.SongBlock;
66  import com.virtuosotechnologies.asaph.model.SongLineMember;
67  import com.virtuosotechnologies.asaph.model.CommentString;
68  import com.virtuosotechnologies.asaph.model.TextString;
69  import com.virtuosotechnologies.asaph.model.ChordAnnotation;
70  import com.virtuosotechnologies.asaph.model.AddedSongBlock;
71  import com.virtuosotechnologies.asaph.model.StandardSongBlock;
72  import com.virtuosotechnologies.asaph.model.notation.Chord;
73  import com.virtuosotechnologies.asaph.model.notation.NotationFactory;
74  import com.virtuosotechnologies.asaph.modelutils.impl.LineFragmentData;
75  import com.virtuosotechnologies.asaph.modelutils.impl.LineListData;
76  import com.virtuosotechnologies.asaph.modelutils.impl.BlockListData;
77  
78  
79  /**
80   * Implementation of DataTransferUtils API
81   */
82  /*package*/ class DataTransferUtilsImpl
83  implements DataTransferUtils
84  {
85    private SongUtilsImpl songUtils_;
86    private NotationManager notationManager_;
87    private ClipboardOwner clipboardOwner_;
88    private Clipboard clipboard_;
89    private Song curClipboardContentsSong_;
90    
91    
92    /*package*/ DataTransferUtilsImpl(
93      NotationManager notationManager)
94    {
95      notationManager_ = notationManager;
96      clipboard_ = Toolkit.getDefaultToolkit().getSystemClipboard();
97      clipboardOwner_ = new ClipboardOwner()
98      {
99        public void lostOwnership(
100         Clipboard clipboard,
101         Transferable contents)
102       {
103         curClipboardContentsSong_ = null;
104       }
105     };
106   }
107   
108   
109   /*package*/ void setSongUtils(
110     SongUtilsImpl songUtils)
111   {
112     songUtils_ = songUtils;
113   }
114   
115   
116   /**
117    * Set the current system clipboard contents.
118    *
119    * @param transfer Transferable to set
120    */
121   public void setClipboardContents(
122     Transferable transfer)
123   {
124     if (transfer instanceof TransferableImpl)
125     {
126       curClipboardContentsSong_ = ((TransferableImpl)transfer).getSong();
127     }
128     clipboard_.setContents(transfer, clipboardOwner_);
129   }
130   
131   
132   /**
133    * Get the current system clipboard contents. If the last call to setClipboardContents
134    * was given a Transferable obtained by one of the methods of this API (i.e. it is a
135    * TransferableImpl), and the clipboard contents haven't changed, then this will
136    * return a TransferableImpl.
137    *
138    * @return Transferable
139    */
140   public Transferable getClipboardContents()
141   {
142     Transferable transfer = clipboard_.getContents(this);
143     if (transfer == null)
144     {
145       return null;
146     }
147     else if (curClipboardContentsSong_ != null)
148     {
149       return new TransferableImpl(transfer, curClipboardContentsSong_);
150     }
151     else
152     {
153       return transfer;
154     }
155   }
156   
157   
158   private LineFragmentData createLineFragment(
159     SongLine line,
160     StringSongLineMember startMember,
161     int startPos,
162     StringSongLineMember endMember,
163     int endPos,
164     ChordSet currentSet,
165     boolean remove,
166     UndoableEditListener undoListener)
167   {
168     if (startMember != null &&
169       (!startMember.getSongLine().equals(line) || startMember.isDefunct()))
170     {
171       throw new IllegalArgumentException();
172     }
173     if (endMember != null &&
174       (!endMember.getSongLine().equals(line) || endMember.isDefunct()))
175     {
176       throw new IllegalArgumentException();
177     }
178     
179     NotationFactory factory = notationManager_.getNotationFactoryForLocale(
180       line.getSong().getLocale());
181     
182     LineFragmentData data = new LineFragmentData();
183     if (startMember != null && startMember.equals(endMember))
184     {
185       String fullStr = startMember.getString();
186       String str = fullStr.substring(startPos, endPos);
187       if (startMember instanceof TextString)
188       {
189         data.addText(str);
190       }
191       else
192       {
193         assert (startMember instanceof CommentString);
194         data.addComment(str);
195       }
196       if (remove)
197       {
198         startMember.setString(fullStr.substring(0, startPos)+
199           fullStr.substring(endPos), undoListener);
200       }
201     }
202     else
203     {
204       SongLineMember member = startMember;
205       if (member == null)
206       {
207         member = line.getNextMember(null);
208       }
209       while (member != null)
210       {
211         if (member.equals(startMember))
212         {
213           String str = startMember.getString();
214           if (remove && startPos < str.length())
215           {
216             startMember.setString(str.substring(0, startPos), undoListener);
217           }
218           if (startPos > 0)
219           {
220             str = str.substring(startPos);
221           }
222           if (startMember instanceof TextString)
223           {
224             data.addText(str);
225           }
226           else
227           {
228             assert (startMember instanceof CommentString);
229             data.addComment(str);
230           }
231           member = line.getNextMember(member);
232         }
233         else if (member.equals(endMember))
234         {
235           String str = endMember.getString();
236           if (remove && endPos > 0)
237           {
238             endMember.setString(str.substring(endPos), undoListener);
239           }
240           if (endPos < str.length())
241           {
242             str = str.substring(0, endPos);
243           }
244           if (endMember instanceof TextString)
245           {
246             data.addText(str);
247           }
248           else
249           {
250             assert (endMember instanceof CommentString);
251             data.addComment(str);
252           }
253           member = null;
254         }
255         else
256         {
257           if (member instanceof TextString)
258           {
259             data.addText(((TextString)member).getString());
260           }
261           else if (member instanceof CommentString)
262           {
263             data.addComment(((CommentString)member).getString());
264           }
265           else
266           {
267             assert (member instanceof ChordAnnotation);
268             ChordAnnotation ann = (ChordAnnotation)member;
269             ChordSet chordSet = ann.getChordSet();
270             String main = ann.getPrimaryChord().generateString();
271             String pre = factory.unparseChordArray(ann.getPrecedingChords());
272             String post = factory.unparseChordArray(ann.getFollowingChords());
273             data.addChord(chordSet, pre, main, post, chordSet.equals(currentSet));
274           }
275           SongLineMember next = line.getNextMember(member);
276           if (remove)
277           {
278             line.removeMember(member, undoListener);
279           }
280           member = next;
281         }
282       }
283     }
284     
285     return data;
286   }
287   
288   
289   /**
290    * Create a Transferable for a fragment of a line
291    *
292    * @param line the containing SongLine
293    * @param startMember the starting line member, or null to specify
294    *     the start of the line
295    * @param startPos position within the starting line member
296    * @param endMember the ending line member, or null to specify
297    *     the end of the line
298    * @param endPos position within the ending line member
299    * @param currentSet current ChordSet
300    * @param remove true to remove the data from the line
301    * @param undoListener listener for undo records. Ignored if remove is false.
302    * @return a Transferable that provides LINEFRAGMENT_FLAVOR
303    * @exception IllegalArgumentException
304    */
305   public Transferable createLineFragmentTransferable(
306     SongLine line,
307     StringSongLineMember startMember,
308     int startPos,
309     StringSongLineMember endMember,
310     int endPos,
311     ChordSet currentSet,
312     boolean remove,
313     UndoableEditListener undoListener)
314   {
315     CollectingCompoundEdit editCollector = null;
316     if (remove && undoListener != null)
317     {
318       editCollector = new CollectingCompoundEdit();
319     }
320     
321     LineFragmentData data = createLineFragment(line, startMember,
322       startPos, endMember, endPos, currentSet, remove, editCollector);
323     Transferable ret = new TransferableImpl(data, line.getSongBlock().getSong());
324     
325     if (editCollector != null)
326     {
327       editCollector.end();
328       if (editCollector.isSignificant())
329       {
330         undoListener.undoableEditHappened(new UndoableEditEvent(this, editCollector));
331       }
332     }
333     return ret;
334   }
335   
336   
337   /**
338    * Create a Transferable for an ordered collection of lines in a song
339    *
340    * @param song the song containing the blocks.
341    * @param lines ordered Collection of Line objects.
342    * @param currentSet current ChordSet
343    * @param remove true to remove the lines from the song
344    * @param undoListener listener for undo records. Ignored if remove is false.
345    * @return a Transferable that provides LINELIST_FLAVOR
346    * @exception IllegalArgumentException some lines are defunct or are not from the
347    *     given song.
348    */
349   public Transferable createLineListTransferable(
350     Song song,
351     Collection lines,
352     ChordSet currentSet,
353     boolean remove,
354     UndoableEditListener undoListener)
355   {
356     for (Iterator iter = lines.iterator(); iter.hasNext(); )
357     {
358       SongLine line = (SongLine)iter.next();
359       if (!line.getSongBlock().getSong().equals(song) || line.isDefunct())
360       {
361         throw new IllegalArgumentException();
362       }
363     }
364     
365     CollectingCompoundEdit editCollector = null;
366     if (remove && undoListener != null)
367     {
368       editCollector = new CollectingCompoundEdit();
369     }
370     
371     LineListData data = new LineListData();
372     for (Iterator iter = lines.iterator(); iter.hasNext(); )
373     {
374       SongLine line = (SongLine)iter.next();
375       LineFragmentData lineData = createLineFragment(line, null, 0, null, 0,
376         currentSet, false, null);
377       data.addLine(lineData, line.getIndentLevel());
378       if (remove)
379       {
380         line.getSongBlock().removeLine(line, editCollector);
381       }
382     }
383     
384     if (editCollector != null)
385     {
386       editCollector.end();
387       if (editCollector.isSignificant())
388       {
389         undoListener.undoableEditHappened(new UndoableEditEvent(this, editCollector));
390       }
391     }
392     return new TransferableImpl(data, song);
393   }
394   
395   
396   /**
397    * Create a Transferable for an ordered collection of blocks in a song
398    *
399    * @param song the song containing the blocks.
400    * @param blocks ordered Collection of Block objects.
401    * @param currentSet current ChordSet
402    * @param variation Variation to remove blocks from
403    * @param remove true to remove the blocks from the song
404    * @param undoListener listener for undo records. Ignored if remove is false.
405    * @return a Transferable that provides BLOCKLIST_FLAVOR
406    * @exception IllegalArgumentException some blocks are defunct or are not from the
407    *     given song.
408    */
409   public Transferable createBlockListTransferable(
410     Song song,
411     Collection blocks,
412     ChordSet currentSet,
413     Variation variation,
414     boolean remove,
415     UndoableEditListener undoListener)
416   {
417     for (Iterator iter = blocks.iterator(); iter.hasNext(); )
418     {
419       SongBlock block = (SongBlock)iter.next();
420       if (!block.getSong().equals(song) || block.isDefunct())
421       {
422         throw new IllegalArgumentException();
423       }
424     }
425     
426     CollectingCompoundEdit editCollector = null;
427     if (remove && undoListener != null)
428     {
429       editCollector = new CollectingCompoundEdit();
430     }
431     
432     BlockListData data = new BlockListData();
433     for (Iterator iter = blocks.iterator(); iter.hasNext(); )
434     {
435       SongBlock block = (SongBlock)iter.next();
436       LineListData blockData = new LineListData();
437       data.addBlock(blockData, block.getIndentLevel());
438       for (SongLine line = block.getNextLine(null); line != null;
439         line = block.getNextLine(line))
440       {
441         LineFragmentData lineData = createLineFragment(line, null, 0, null, 0,
442           currentSet, false, null);
443         blockData.addLine(lineData, line.getIndentLevel());
444       }
445       if (remove)
446       {
447         if (variation == null || (block instanceof AddedSongBlock))
448         {
449           song.removeBlock(block, editCollector);
450         }
451         else
452         {
453           ((StandardSongBlock)block).omitVariation(variation, editCollector);
454         }
455       }
456     }
457     
458     if (editCollector != null)
459     {
460       editCollector.end();
461       if (editCollector.isSignificant())
462       {
463         undoListener.undoableEditHappened(new UndoableEditEvent(this, editCollector));
464       }
465     }
466     return new TransferableImpl(data, song);
467   }
468   
469   
470   /**
471    * Get the flavor of the given transferable. If the given preferred flavor is
472    * available in the transferable, returns that flavor, otherwise, returns the "best"
473    * flavor out of LINFRAGMENT_FLAVOR, LINELIST_FLAVOR, BLOCKLIST_FLAVOR and stringFlavor.
474    * Returns null if none of those flavors is available.
475    *
476    * @param transferable transferable to examine
477    * @param preferred the preferred flavor, or null for no preference.
478    * @return the DataFlavor to use for pasting.
479    */
480   public DataFlavor getFlavorOf(
481     Transferable transferable,
482     DataFlavor preferred)
483   {
484     if (LINEFRAGMENT_FLAVOR.equals(preferred))
485     {
486       if (transferable.isDataFlavorSupported(LINEFRAGMENT_FLAVOR))
487       {
488         return LINEFRAGMENT_FLAVOR;
489       }
490       if (transferable.isDataFlavorSupported(LINELIST_FLAVOR))
491       {
492         return LINELIST_FLAVOR;
493       }
494       if (transferable.isDataFlavorSupported(BLOCKLIST_FLAVOR))
495       {
496         return BLOCKLIST_FLAVOR;
497       }
498       if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor))
499       {
500         return DataFlavor.stringFlavor;
501       }
502     }
503     else if (LINELIST_FLAVOR.equals(preferred))
504     {
505       if (transferable.isDataFlavorSupported(LINELIST_FLAVOR))
506       {
507         return LINELIST_FLAVOR;
508       }
509       if (transferable.isDataFlavorSupported(LINEFRAGMENT_FLAVOR))
510       {
511         return LINEFRAGMENT_FLAVOR;
512       }
513       if (transferable.isDataFlavorSupported(BLOCKLIST_FLAVOR))
514       {
515         return BLOCKLIST_FLAVOR;
516       }
517       if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor))
518       {
519         return DataFlavor.stringFlavor;
520       }
521     }
522     else if (BLOCKLIST_FLAVOR.equals(preferred))
523     {
524       if (transferable.isDataFlavorSupported(BLOCKLIST_FLAVOR))
525       {
526         return BLOCKLIST_FLAVOR;
527       }
528       if (transferable.isDataFlavorSupported(LINELIST_FLAVOR))
529       {
530         return LINELIST_FLAVOR;
531       }
532       if (transferable.isDataFlavorSupported(LINEFRAGMENT_FLAVOR))
533       {
534         return LINEFRAGMENT_FLAVOR;
535       }
536       if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor))
537       {
538         return DataFlavor.stringFlavor;
539       }
540     }
541     else if (DataFlavor.stringFlavor.equals(preferred))
542     {
543       if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor))
544       {
545         return DataFlavor.stringFlavor;
546       }
547       if (transferable.isDataFlavorSupported(LINEFRAGMENT_FLAVOR))
548       {
549         return LINEFRAGMENT_FLAVOR;
550       }
551       if (transferable.isDataFlavorSupported(LINELIST_FLAVOR))
552       {
553         return LINELIST_FLAVOR;
554       }
555       if (transferable.isDataFlavorSupported(BLOCKLIST_FLAVOR))
556       {
557         return BLOCKLIST_FLAVOR;
558       }
559     }
560     else
561     {
562       if (transferable.isDataFlavorSupported(LINEFRAGMENT_FLAVOR))
563       {
564         return LINEFRAGMENT_FLAVOR;
565       }
566       if (transferable.isDataFlavorSupported(LINELIST_FLAVOR))
567       {
568         return LINELIST_FLAVOR;
569       }
570       if (transferable.isDataFlavorSupported(BLOCKLIST_FLAVOR))
571       {
572         return BLOCKLIST_FLAVOR;
573       }
574       if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor))
575       {
576         return DataFlavor.stringFlavor;
577       }
578     }
579     return null;
580   }
581   
582   
583   /**
584    * Create a Transferable given a string. Interprets the string into lines, and
585    * generates either LINFRAGMENT_FLAVOR or LINELIST_FLAVOR.
586    *
587    * @param str string to examine
588    * @return a Transferable
589    */
590   public Transferable createTransferableForString(
591     String str)
592   {
593     if (str.indexOf('\n') == -1)
594     {
595       LineFragmentData data = new LineFragmentData();
596       data.addUntypedString(str);
597       return new TransferableImpl(data, null);
598     }
599     
600     LineListData data = new LineListData();
601     char[] chars = str.toCharArray();
602     StringBuffer buf = new StringBuffer();
603     for (int i=0; i<chars.length; )
604     {
605       char ch = chars[i++];
606       if (ch == '\n' || ch == '\r')
607       {
608         LineFragmentData frag = new LineFragmentData();
609         frag.addUntypedString(new String(buf));
610         data.addLine(frag, 0);
611         buf = new StringBuffer();
612         if (ch == '\r' && i < chars.length && chars[i] == '\n')
613         {
614           ++i;
615         }
616       }
617       else if (ch >= 32 && ch != 127)
618       {
619         buf.append(ch);
620       }
621     }
622     if (buf.length() > 0)
623     {
624       LineFragmentData frag = new LineFragmentData();
625       frag.addUntypedString(new String(buf));
626       data.addLine(frag, 0);
627     }
628     return new TransferableImpl(data, null);
629   }
630   
631   
632   /**
633    * Get the type of object that would be pasted given a transferable and a context.
634    *
635    * @param transferable transferable to examine
636    * @param pasteContext the DataFlavor indicating where the user is pasting. Must be
637    *     LINFRAGMENT_FLAVOR, LINELIST_FLAVOR or BLOCKLIST_FLAVOR.
638    * @return a DataFlavor indicating the type of pasted object
639    * @exception CannotPasteException the transferable doesn't provide a suitable flavor
640    */
641   public DataFlavor getPasteType(
642     Transferable transferable,
643     DataFlavor pasteContext)
644   throws
645     CannotPasteException
646   {
647     if (!pasteContext.equals(BLOCKLIST_FLAVOR) && !pasteContext.equals(LINELIST_FLAVOR) &&
648       !pasteContext.equals(LINEFRAGMENT_FLAVOR))
649     {
650       throw new IllegalArgumentException();
651     }
652     DataFlavor flavor = getFlavorOf(transferable, pasteContext);
653     if (flavor == null)
654     {
655       throw new CannotPasteException();
656     }
657     if (flavor.equals(DataFlavor.stringFlavor))
658     {
659       try
660       {
661         String str = (String)transferable.getTransferData(DataFlavor.stringFlavor);
662         transferable = createTransferableForString(str);
663         flavor = getFlavorOf(transferable, pasteContext);
664         if (flavor == null || flavor.equals(DataFlavor.stringFlavor))
665         {
666           // Unexpected
667           throw new CannotPasteException();
668         }
669       }
670       catch (UnsupportedFlavorException ex)
671       {
672         // Unexpected
673         throw new CannotPasteException(ex);
674       }
675       catch (IOException ex)
676       {
677         throw new CannotPasteException(ex);
678       }
679     }
680     
681     if (flavor.equals(BLOCKLIST_FLAVOR) || pasteContext.equals(BLOCKLIST_FLAVOR))
682     {
683       return BLOCKLIST_FLAVOR;
684     }
685     if (flavor.equals(LINELIST_FLAVOR) || pasteContext.equals(LINELIST_FLAVOR))
686     {
687       return LINELIST_FLAVOR;
688     }
689     assert flavor.equals(LINEFRAGMENT_FLAVOR);
690     assert pasteContext.equals(LINEFRAGMENT_FLAVOR);
691     return LINEFRAGMENT_FLAVOR;
692   }
693   
694   
695   private void insertLineFragmentAt(
696     LineFragmentData data,
697     SongLine line,
698     StringSongLineMember member,
699     int pos,
700     boolean sameSong,
701     ChordSet chordSet,
702     UndoableEditListener undoListener)
703   {
704     if (data.size() == 0)
705     {
706       return;
707     }
708     
709     Song song = line.getSongBlock().getSong();
710     NotationFactory factory = notationManager_.getNotationFactoryForLocale(song.getLocale());
711     
712     StringSongLineMember afterMember = null;
713     if (member != null)
714     {
715       String str = member.getString();
716       member.setString(str.substring(0, pos), undoListener);
717       if (member instanceof TextString)
718       {
719         afterMember = line.insertTextStringAfter(member,
720           str.substring(pos), undoListener);
721       }
722       else
723       {
724         assert (member instanceof CommentString);
725         afterMember = line.insertCommentStringAfter(member,
726           str.substring(pos), undoListener);
727       }
728     }
729     
730     SongLineMember lastMember = member;
731     for (Iterator iter = data.iterator(); iter.hasNext(); )
732     {
733       Object frag = iter.next();
734       if (frag instanceof LineFragmentData.TextData)
735       {
736         LineFragmentData.TextData textFrag = (LineFragmentData.TextData)frag;
737         lastMember = line.insertTextStringAfter(lastMember,
738           textFrag.getData(), undoListener);
739       }
740       else if (frag instanceof LineFragmentData.CommentData)
741       {
742         LineFragmentData.CommentData commentFrag = (LineFragmentData.CommentData)frag;
743         lastMember = line.insertCommentStringAfter(lastMember,
744           commentFrag.getData(), undoListener);
745       }
746       else if (frag instanceof LineFragmentData.UntypedStringData)
747       {
748         LineFragmentData.UntypedStringData stringFrag = (LineFragmentData.UntypedStringData)frag;
749         if (member instanceof CommentString)
750         {
751           lastMember = line.insertCommentStringAfter(lastMember,
752             stringFrag.getData(), undoListener);
753         }
754         else
755         {
756           lastMember = line.insertTextStringAfter(lastMember,
757             stringFrag.getData(), undoListener);
758         }
759       }
760       else
761       {
762         LineFragmentData.ChordData chordFrag = (LineFragmentData.ChordData)frag;
763         Chord main = factory.parseChord(chordFrag.getMain());
764         Chord[] pre = factory.parseChordArray(chordFrag.getPre());
765         Chord[] post = factory.parseChordArray(chordFrag.getPost());
766         if (sameSong)
767         {
768           ChordSet set = chordFrag.getChordSetIn(song);
769           if (set != null)
770           {
771             lastMember = line.insertChordAnnotationAfter(lastMember,
772               set, main, pre, post, undoListener);
773           }
774         }
775         else if (chordSet != null && chordFrag.isCurrentChordSet())
776         {
777           lastMember = line.insertChordAnnotationAfter(lastMember,
778             chordSet, main, pre, post, undoListener);
779         }
780       }
781     }
782     
783     // Compact line at the member and afterMember
784 /*    if (afterMember == null)
785     {
786       afterMember = (StringSongLineMember)line.getNextMember(lastMember);
787     }
788     StringSongLineMember first = (StringSongLineMember)line.getNextMember(member);
789     StringSongLineMember last = (StringSongLineMember)line.getPreviousMember(afterMember);
790     if (first.equals(last))
791     {
792       if (((first instanceof TextString) && (member instanceof TextString)) ||
793         ((first instanceof CommentString) && (member instanceof CommentString)))
794       {
795         member.setString(member.getString()+first.getString()+
796           afterMember.getString(), undoListener);
797         line.removeMember(first, undoListener);
798         line.removeMember(afterMember, undoListener);
799       }
800       else if (member == null &&
801         (((last instanceof TextString) && (afterMember instanceof TextString)) ||
802         ((last instanceof CommentString) && (afterMember instanceof CommentString))))
803       {
804         afterMember.setString(last.getString()+afterMember.getString(), undoListener);
805         line.removeMember(last, undoListener);
806       }
807     }
808     else
809     {
810       if (((first instanceof TextString) && (member instanceof TextString)) ||
811         ((first instanceof CommentString) && (member instanceof CommentString)))
812       {
813         member.setString(member.getString()+first.getString(), undoListener);
814         line.removeMember(first, undoListener);
815       }
816       if (((last instanceof TextString) && (afterMember instanceof TextString)) ||
817         ((last instanceof CommentString) && (afterMember instanceof CommentString)))
818       {
819         afterMember.setString(last.getString()+afterMember.getString(), undoListener);
820         line.removeMember(last, undoListener);
821       }
822     }*/
823     songUtils_.compactLine(line, undoListener);
824   }
825   
826   
827   private void insertLineListAfter(
828     LineListData data,
829     SongBlock block,
830     SongLine line,
831     boolean sameSong,
832     ChordSet chordSet,
833     UndoableEditListener undoListener)
834   {
835     SongLine lastLine = line;
836     for (Iterator iter = data.iterator(); iter.hasNext(); )
837     {
838       LineListData.LineData lineData = (LineListData.LineData)iter.next();
839       lastLine = block.insertLineAfter(lastLine, undoListener);
840       lastLine.setIndentLevel(lineData.getIndent(), undoListener);
841       insertLineFragmentAt(lineData.getLineFragmentData(),
842         lastLine, null, 0, sameSong, chordSet, undoListener);
843     }
844   }
845   
846   
847   private void insertBlockListAfter(
848     BlockListData data,
849     Song song,
850     SongBlock block,
851     boolean sameSong,
852     ChordSet chordSet,
853     Variation variation,
854     UndoableEditListener undoListener)
855   {
856     SongBlock lastBlock = block;
857     for (Iterator iter = data.iterator(); iter.hasNext(); )
858     {
859       BlockListData.BlockData blockData = (BlockListData.BlockData)iter.next();
860       lastBlock = song.insertBlockAfter(lastBlock, variation, undoListener);
861       lastBlock.setIndentLevel(blockData.getIndent(), undoListener);
862       insertLineListAfter(blockData.getLineListData(),
863         lastBlock, null, sameSong, chordSet, undoListener);
864     }
865   }
866   
867   
868   /**
869    * Paste the given transferable in the given position.
870    *
871    * @param transferable transferable to paste
872    * @param line line to paste into
873    * @param member member containing the paste position. Null to paste at the beginning
874    * @param pos position within the member
875    * @param chordSet ChordSet to use for the transferable's primary chords
876    *     if the destination song is not the same as the source song.
877    * @param variation variation to add blocks to, or null for the default variation.
878    * @param undoListener listener for undo records, or null to not generate edits
879    * @exception CannotPasteException the transferable doesn't provide a suitable flavor
880    */
881   public void pasteTransferable(
882     Transferable transferable,
883     SongLine line,
884     StringSongLineMember member,
885     int pos,
886     ChordSet chordSet,
887     Variation variation,
888     UndoableEditListener undoListener)
889   throws
890     CannotPasteException
891   {
892     pasteTransferableImpl(transferable, line, member, pos, false, null, 0, null, 0,
893       chordSet, variation, undoListener);
894   }
895   
896   
897   /**
898    * Paste the given transferable, replacing the given range.
899    *
900    * @param transferable transferable to paste
901    * @param line line to paste into
902    * @param startMember Start of the range to replace. Null for the beginning of the line
903    * @param startPos position within startMember
904    * @param endMember End of the range to replace. Null for the end of the line
905    * @param endPos position within endMember
906    * @param chordSet ChordSet to use for the transferable's primary chords
907    *     if the destination song is not the same as the source song.
908    * @param variation variation to add blocks to, or null for the default variation.
909    * @param undoListener listener for undo records, or null to not generate edits
910    * @exception CannotPasteException the transferable doesn't provide a suitable flavor
911    */
912   public void pasteTransferable(
913     Transferable transferable,
914     SongLine line,
915     StringSongLineMember startMember,
916     int startPos,
917     StringSongLineMember endMember,
918     int endPos,
919     ChordSet chordSet,
920     Variation variation,
921     UndoableEditListener undoListener)
922   throws
923     CannotPasteException
924   {
925     pasteTransferableImpl(transferable, line, startMember, startPos, true,
926       startMember, startPos, endMember, endPos, chordSet, variation, undoListener);
927   }
928   
929   
930   private void pasteTransferableImpl(
931     Transferable transferable,
932     SongLine line,
933     StringSongLineMember member,
934     int pos,
935     boolean delete,
936     StringSongLineMember startMember,
937     int startPos,
938     StringSongLineMember endMember,
939     int endPos,
940     ChordSet chordSet,
941     Variation variation,
942     UndoableEditListener undoListener)
943   throws
944     CannotPasteException
945   {
946     DataFlavor flavor = getFlavorOf(transferable, LINEFRAGMENT_FLAVOR);
947     if (flavor == null)
948     {
949       throw new CannotPasteException();
950     }
951     if (flavor.equals(DataFlavor.stringFlavor))
952     {
953       try
954       {
955         String str = (String)transferable.getTransferData(DataFlavor.stringFlavor);
956         transferable = createTransferableForString(str);
957         flavor = getFlavorOf(transferable, LINEFRAGMENT_FLAVOR);
958         if (flavor == null || flavor.equals(DataFlavor.stringFlavor))
959         {
960           // Unexpected
961           throw new CannotPasteException();
962         }
963       }
964       catch (UnsupportedFlavorException ex)
965       {
966         // Unexpected
967         throw new CannotPasteException(ex);
968       }
969       catch (IOException ex)
970       {
971         throw new CannotPasteException(ex);
972       }
973     }
974     
975     CollectingCompoundEdit editCollector = null;
976     if (undoListener != null)
977     {
978       editCollector = new CollectingCompoundEdit();
979     }
980     
981     if (delete)
982     {
983       songUtils_.removeLineRange(line, startMember, startPos, endMember, endPos, editCollector);
984     }
985     
986     boolean sameSong = false;
987     if (transferable instanceof TransferableImpl)
988     {
989       sameSong = line.getSongBlock().getSong().equals(((TransferableImpl)transferable).getSong());
990     }
991     
992     try
993     {
994       if (flavor.equals(LINEFRAGMENT_FLAVOR))
995       {
996         LineFragmentData data = (LineFragmentData)
997           transferable.getTransferData(LINEFRAGMENT_FLAVOR);
998         insertLineFragmentAt(data, line, member, pos, sameSong, chordSet, editCollector);
999       }
1000      else if (flavor.equals(LINELIST_FLAVOR))
1001      {
1002        LineListData data = (LineListData)
1003          transferable.getTransferData(LINELIST_FLAVOR);
1004        songUtils_.splitLine(member, pos, editCollector);
1005        insertLineListAfter(data, line.getSongBlock(), line, sameSong, chordSet, editCollector);
1006      }
1007      else if (flavor.equals(BLOCKLIST_FLAVOR))
1008      {
1009        BlockListData data = (BlockListData)
1010          transferable.getTransferData(BLOCKLIST_FLAVOR);
1011        songUtils_.splitLine(member, pos, editCollector);
1012        SongBlock firstBlock = line.getSongBlock();
1013        songUtils_.splitBlock(firstBlock, line, editCollector);
1014        insertBlockListAfter(data, firstBlock.getSong(), firstBlock, sameSong, chordSet, variation, editCollector);
1015      }
1016    }
1017    catch (UnsupportedFlavorException ex)
1018    {
1019      throw new CannotPasteException(ex);
1020    }
1021    catch (IOException ex)
1022    {
1023      throw new CannotPasteException(ex);
1024    }
1025    
1026    if (editCollector != null)
1027    {
1028      editCollector.end();
1029      if (editCollector.isSignificant())
1030      {
1031        undoListener.undoableEditHappened(new UndoableEditEvent(this, editCollector));
1032      }
1033    }
1034  }
1035  
1036  
1037  /**
1038   * Paste the given transferable after the given line.
1039   *
1040   * @param transferable transferable to paste
1041   * @param block block to paste into
1042   * @param line line to paste after, or null to paste at beginning
1043   * @param chordSet ChordSet to use for the transferable's primary chords
1044   *     if the destination song is not the same as the source song.
1045   * @param variation variation to add blocks to, or null for the default variation.
1046   * @param undoListener listener for undo records, or null to not generate edits
1047   * @exception CannotPasteException the transferable doesn't provide a suitable flavor
1048   */
1049  public void pasteTransferableAfter(
1050    Transferable transferable,
1051    SongBlock block,
1052    SongLine line,
1053    ChordSet chordSet,
1054    Variation variation,
1055    UndoableEditListener undoListener)
1056  throws
1057    CannotPasteException
1058  {
1059    DataFlavor flavor = getFlavorOf(transferable, LINELIST_FLAVOR);
1060    if (flavor == null)
1061    {
1062      throw new CannotPasteException();
1063    }
1064    if (flavor.equals(DataFlavor.stringFlavor))
1065    {
1066      try
1067      {
1068        String str = (String)transferable.getTransferData(DataFlavor.stringFlavor);
1069        transferable = createTransferableForString(str);
1070        flavor = getFlavorOf(transferable, LINELIST_FLAVOR);
1071        if (flavor == null || flavor.equals(DataFlavor.stringFlavor))
1072        {
1073          // Unexpected
1074          throw new CannotPasteException();
1075        }
1076      }
1077      catch (UnsupportedFlavorException ex)
1078      {
1079        // Unexpected
1080        throw new CannotPasteException(ex);
1081      }
1082      catch (IOException ex)
1083      {
1084        throw new CannotPasteException(ex);
1085      }
1086    }
1087    
1088    CollectingCompoundEdit editCollector = null;
1089    if (undoListener != null)
1090    {
1091      editCollector = new CollectingCompoundEdit();
1092    }
1093    
1094    boolean sameSong = false;
1095    if (transferable instanceof TransferableImpl)
1096    {
1097      sameSong = block.getSong().equals(((TransferableImpl)transferable).getSong());
1098    }
1099    
1100    try
1101    {
1102      if (flavor.equals(LINEFRAGMENT_FLAVOR))
1103      {
1104        LineFragmentData data = (LineFragmentData)
1105          transferable.getTransferData(LINEFRAGMENT_FLAVOR);
1106        SongLine nline = block.insertLineAfter(line, editCollector);
1107        insertLineFragmentAt(data, nline, null, 0, sameSong, chordSet, editCollector);
1108      }
1109      else if (flavor.equals(LINELIST_FLAVOR))
1110      {
1111        LineListData data = (LineListData)
1112          transferable.getTransferData(LINELIST_FLAVOR);
1113        insertLineListAfter(data, block, line, sameSong, chordSet, editCollector);
1114      }
1115      else if (flavor.equals(BLOCKLIST_FLAVOR))
1116      {
1117        BlockListData data = (BlockListData)
1118          transferable.getTransferData(BLOCKLIST_FLAVOR);
1119        songUtils_.splitBlock(block, line, editCollector);
1120        insertBlockListAfter(data, block.getSong(), block, sameSong, chordSet, variation, editCollector);
1121      }
1122    }
1123    catch (UnsupportedFlavorException ex)
1124    {
1125      throw new CannotPasteException(ex);
1126    }
1127    catch (IOException ex)
1128    {
1129      throw new CannotPasteException(ex);
1130    }
1131    
1132    if (editCollector != null)
1133    {
1134      editCollector.end();
1135      if (editCollector.isSignificant())
1136      {
1137        undoListener.undoableEditHappened(new UndoableEditEvent(this, editCollector));
1138      }
1139    }
1140  }
1141  
1142  
1143  /**
1144   * Paste the given transferable after the given block.
1145   *
1146   * @param transferable transferable to paste
1147   * @param song song to paste into
1148   * @param block block to paste after, or null to paste at beginning
1149   * @param block position to paste
1150   * @param chordSet ChordSet to use for the transferable's primary chords
1151   *     if the destination song is not the same as the source song.
1152   * @param variation variation to add blocks to, or null for the default variation.
1153   * @param undoListener listener for undo records, or null to not generate edits
1154   * @exception CannotPasteException the transferable doesn't provide a suitable flavor
1155   */
1156  public void pasteTransferableAfter(
1157    Transferable transferable,
1158    Song song,
1159    SongBlock block,
1160    ChordSet chordSet,
1161    Variation variation,
1162    UndoableEditListener undoListener)
1163  throws
1164    CannotPasteException
1165  {
1166    DataFlavor flavor = getFlavorOf(transferable, BLOCKLIST_FLAVOR);
1167    if (flavor == null)
1168    {
1169      throw new CannotPasteException();
1170    }
1171    if (flavor.equals(DataFlavor.stringFlavor))
1172    {
1173      try
1174      {
1175        String str = (String)transferable.getTransferData(DataFlavor.stringFlavor);
1176        transferable = createTransferableForString(str);
1177        flavor = getFlavorOf(transferable, BLOCKLIST_FLAVOR);
1178        if (flavor == null || flavor.equals(DataFlavor.stringFlavor))
1179        {
1180          // Unexpected
1181          throw new CannotPasteException();
1182        }
1183      }
1184      catch (UnsupportedFlavorException ex)
1185      {
1186        // Unexpected
1187        throw new CannotPasteException(ex);
1188      }
1189      catch (IOException ex)
1190      {
1191        throw new CannotPasteException(ex);
1192      }
1193    }
1194    
1195    CollectingCompoundEdit editCollector = null;
1196    if (undoListener != null)
1197    {
1198      editCollector = new CollectingCompoundEdit();
1199    }
1200    
1201    boolean sameSong = false;
1202    if (transferable instanceof TransferableImpl)
1203    {
1204      sameSong = song.equals(((TransferableImpl)transferable).getSong());
1205    }
1206    
1207    try
1208    {
1209      if (flavor.equals(LINEFRAGMENT_FLAVOR))
1210      {
1211        LineFragmentData data = (LineFragmentData)
1212          transferable.getTransferData(LINEFRAGMENT_FLAVOR);
1213        SongBlock nblock = song.insertBlockAfter(block, variation, editCollector);
1214        SongLine nline = nblock.insertLineAfter(null, editCollector);
1215        insertLineFragmentAt(data, nline, null, 0, sameSong, chordSet, editCollector);
1216      }
1217      else if (flavor.equals(LINELIST_FLAVOR))
1218      {
1219        LineListData data = (LineListData)
1220          transferable.getTransferData(LINELIST_FLAVOR);
1221        SongBlock nblock = song.insertBlockAfter(block, variation, editCollector);
1222        insertLineListAfter(data, nblock, null, sameSong, chordSet, editCollector);
1223      }
1224      else if (flavor.equals(BLOCKLIST_FLAVOR))
1225      {
1226        BlockListData data = (BlockListData)
1227          transferable.getTransferData(BLOCKLIST_FLAVOR);
1228        insertBlockListAfter(data, song, block, sameSong, chordSet, variation, editCollector);
1229      }
1230    }
1231    catch (UnsupportedFlavorException ex)
1232    {
1233      throw new CannotPasteException(ex);
1234    }
1235    catch (IOException ex)
1236    {
1237      throw new CannotPasteException(ex);
1238    }
1239    
1240    if (editCollector != null)
1241    {
1242      editCollector.end();
1243      if (editCollector.isSignificant())
1244      {
1245        undoListener.undoableEditHappened(new UndoableEditEvent(this, editCollector));
1246      }
1247    }
1248  }
1249}