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}